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

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