Тема далеко не новая, но часто упускаемая при обучении программированию, поэтому повторить лишний раз не помешает.
Допустим, вы хотите посчитать вещественный квадратный корень из модуля представимого в машинной архитектуре целого числа 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
.