Pull to refresh

Негативное свойство отрицательных чисел

Level of difficultyEasy
Reading time2 min
Views4.4K

Тема далеко не новая, но часто упускаемая при обучении программированию, поэтому повторить лишний раз не помешает.

Допустим, вы хотите посчитать вещественный квадратный корень из модуля представимого в машинной архитектуре целого числа n, значение которого может быть любым. Какой код вы для этого напишете (используем синтаксис языка Си, хотя язык здесь не важен)?

1) double f = sqrt( abs(n) );

2) double f = sqrt( (double) abs(n) );

3) double f = sqrt( fabs( (double)n ) );

Разместим здесь КДПВ, чтобы у желающих было время поразмыслить.

Единственным корректным кодом при заданных условиях является ответ номер 3. Для значения n, равного минимальному отрицательному числу (то есть, например, -2147483648 для 32-разрядного типа int), два других ответа дадут значение NaN или приведут к аварийному завершению программы, в зависимости от архитектуры процессора. Строго говоря, это неопределённое поведение.

Почему так происходит? Опытные программисты, конечно, всё поняли, а для менее опытных объясним.

Отрицательные целые числа во всех современных процессорах представляются в дополнительном коде. Преимуществом дополнительного кода перед прямым и обратным кодом является только одно значение для кодирования нуля вместо раздельных +0 и -0. Но так как ноль у нас один, а общее количество значений, кодируемых двоичными разрядами, очевидно, чётно, то получается, что для одного положительного или отрицательного значения в любом случае не найдётся пары среди значений с другим знаком. И действительно, минимальное отрицательное число (-128, -32768, -2147483648 и т.д., в зависимости от разрядности целых) не имеет представимой положительной пары своей разрядности (+128, +32768, +2147483648 и т.д.).

Что же происходит при попытке обратить знак такого непарного числа? А ничего:

-(-2147483648) == -2147483648

abs (-2147483648) == -2147483648

Поэтому наша попытка обезопасить вычисление sqrt при помощи abs проваливается на таком значении, и мы получаем квадратный корень из отрицательного числа. Ну и во всех остальных алгоритмах возможна такая же ерунда.

В вещественных числах такого не происходит, так как они кодируются в прямом коде, и несимметричных вещественных значений нет. Зато есть +0.0 и -0.0.

Tags:
Hubs:
Total votes 9: ↑7 and ↓2+5
Comments19

Articles