Ответ 1
Вы спрашиваете о С++, но особенности значений и кодировок с плавающей запятой определяются спецификацией с плавающей запятой, в частности IEEE 754, а не С++. IEEE 754 на сегодняшний день является наиболее широко используемой спецификацией с плавающей запятой, и я отвечу на ее использование.
В IEEE 754 двоичные значения с плавающей запятой кодируются тремя частями: бит знака s (0 для положительного, 1 для отрицательного), смещенный показатель e (представленный показатель плюс фиксированное смещение) и значимое поле f (часть дроби). Для нормальных чисел они представляют собой точно число (-1) s • 2 e - bias • 1. f, где 1. f - двоичная цифра, сформированная путем записи значащих бит после "1.". (Например, если значение поля имеет десять бит 0010111011, оно представляет значение 1.0010111011 2, которое равно 1.182617175 или 1211/1024.)
Отклонение зависит от формата с плавающей запятой. Для 64-битного двоичного кода IEEE 754 поле экспоненты имеет 11 бит, а смещение равно 1023. Когда фактический показатель равен 0, закодированное поле экспоненты составляет 1023. Фактические показатели -2, -1, 0, 1 и 2 имеют закодированные показатели 1021, 1022, 1023, 1024 и 1025. Когда кто-то говорит об экспоненте нулевого числа, равного нулю, это означает, что закодированный показатель равен нулю. Фактический показатель будет меньше -1022. Для 64-битного нормального интервала экспоненты составляет от -1022 до 1023 (закодированные значения от 1 до 2046). Когда экспонента перемещается за этот интервал, происходят особые вещи.
Над этим интервалом остановки с плавающей запятой представляют конечные числа. Закодированный показатель 2047 (все 1 бит) представляет бесконечность (при значении поля, установленном на ноль). Ниже этого диапазона значения с плавающей запятой изменяются на ненормальные числа. Когда закодированный показатель равен нулю, значение поля значения равно 0. f вместо 1. f.
Для этого есть важная причина. Если наименьшее значение экспоненты было просто другим нормальным кодированием, то младшие биты его значения были бы слишком малы, чтобы представлять их как значения с плавающей запятой. Без этого ведущего "1." не было бы возможности сказать, где первый 1 бит. Например, предположим, что у вас было два числа, как с наименьшим показателем, так и с знаками 1.0010111011 2 и 1.0000000000 2. Когда вы вычитаете значения, результат равен .0010111011 2. К сожалению, нет возможности представить это как нормальное число. Поскольку вы уже были на самом низком экспоненте, вы не можете представить нижний показатель, который необходим, чтобы сказать, где первый результат в этом результате. Поскольку математический результат слишком мал для представления, компьютер будет вынужден вернуть ближайшее представимое число, которое будет равно нулю.
Это создает нежелательное свойство в системе с плавающей запятой, которое может иметь a != b
, но a-b == 0
. Чтобы избежать этого, используются ненормальные числа. Используя субнормальные числа, мы имеем специальный интервал, где фактический показатель не уменьшается, и мы можем выполнять арифметику, не создавая слишком малых чисел для представления. Когда закодированный показатель равен нулю, фактический показатель является таким же, как и при кодированном экспоненте, но значение знаменателя изменяется на 0. f вместо 1. f. Когда мы это делаем, a != b
гарантирует, что вычисленное значение a-b
не равно нулю.
Вот комбинации значений в кодировках 64-битной двоичной с плавающей запятой IEEE 754:
Sign Exponent (e) Significand Bits (f) Meaning 0 0 0 +zero 0 0 Non-zero +2-1022•0.f (subnormal) 0 1 to 2046 Anything +2e-1023•1.f (normal) 0 2047 0 +infinity 0 2047 Non-zero but high bit off +, signaling NaN 0 2047 High bit on +, quiet NaN 1 0 0 -zero 1 0 Non-zero -2-1022•0.f (subnormal) 1 1 to 2046 Anything -2e-1023•1.f (normal) 1 2047 0 -infinity 1 2047 Non-zero but high bit off -, signaling NaN 1 2047 High bit on -, quiet NaN
Некоторые примечания:
+0 и -0 математически равны, но знак сохраняется. Тщательно написанные приложения могут использовать его в определенных особых ситуациях.
NaN означает "Не номер". Как правило, это означает, что произошел нематематический результат или другая ошибка, и вычисление должно быть отброшено или переделано другим способом. Как правило, операция с NaN дает другое NaN, таким образом сохраняя информацию о том, что что-то пошло не так. Например, 3 + NaN
создает NaN. Сигнальная NaN предназначена для того, чтобы вызвать исключение либо для указания, что программа пошла не так, либо для того, чтобы другое программное обеспечение (например, отладчик) выполняло какое-то специальное действие. Тихий NaN предназначен для распространения до дальнейших результатов, что позволяет завершить остальную часть большого вычисления в случаях, когда NaN является лишь частью большого набора данных и будет обрабатываться отдельно позже или будет отброшена.
Знаки, + и -, сохраняются с NaN, но не имеют математического значения.
В обычном программировании вы не должны беспокоиться о кодировании с плавающей запятой, за исключением того, что он информирует вас о границах и поведении вычислений с плавающей запятой. Вам не нужно делать ничего особенного в отношении ненормальных чисел.
К сожалению, некоторые процессоры сломаны тем, что они либо нарушают стандарт IEEE 754, изменяя подстрочные числа на ноль, либо они выполняются очень медленно при использовании субнормальных чисел. При программировании для таких процессоров вы можете попытаться избежать использования ненормальных чисел.