Что это за "денормальные данные"? - С++

Я хотел бы иметь широкое представление о "денормальных данных" и о том, что это такое, потому что единственное, что, по-моему, я получил, - это тот факт, что это что-то особенное, связанное с значениями с плавающей запятой с точки зрения программиста, и связано с общий подход с вычислительной точки зрения.

Кто-то может расшифровать эти 2 слова для меня?

Спасибо.

ИЗМЕНИТЬ

помните, что я ориентирован на С++-приложения и только на С++-язык.

Ответы

Ответ 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, изменяя подстрочные числа на ноль, либо они выполняются очень медленно при использовании субнормальных чисел. При программировании для таких процессоров вы можете попытаться избежать использования ненормальных чисел.

Ответ 2

Чтобы понять нормальные значения с плавающей запятой, вам сначала нужно понять нормальные. Значение с плавающей запятой имеет мантисса и показатель степени. В десятичном значении, например 1.2345E6, 1.2345 - мантисса, 6 - показатель степени. Хорошая идея о нотации с плавающей запятой заключается в том, что вы всегда можете ее нормализовать. Как и 0,012345E8 и 0,12345E7, это то же значение, что и 1.2345E6. Или, другими словами, вы всегда можете сделать первую цифру мантиссы отличным от нуля числом, если значение не равно нулю.

Компьютеры хранят значения с плавающей запятой в двоичном формате, цифры равны 0 или 1. Таким образом, свойство двоичного значения с плавающей запятой, которое не равно нулю, состоит в том, что его всегда можно записать, начиная с 1.

Это очень привлекательная цель оптимизации. Поскольку значение всегда начинается с 1, нет смысла хранить это. 1. Что приятно в том, что вы фактически получаете дополнительный бит точности бесплатно. В 64-битном двойнике мантисса имеет 52 бит памяти. Фактическая точность составляет 53 бит благодаря предполагаемому 1.

Мы должны поговорить о наименьшем возможном значении с плавающей запятой, которое вы можете сохранить таким образом. Выполняя его в десятичном порядке, если у вас был десятичный процессор с 5 разрядами памяти в мантиссе и 2 в экспоненте, то наименьшее значение, которое оно может сохранить, равно нулю, равно 1.00000E-99. С 1 является подразумеваемой цифрой, которая не хранится (не работает в десятичной форме, но несет со мной). Таким образом, мантисса хранит 00000, а экспоненты хранят -99. Вы не можете сохранить меньшее число, показатель экспоненты превышен до -99.

Ну, ты можешь. Вы можете отказаться от нормализованного представления и забыть о предполагаемой оптимизации цифр. Вы можете сохранить его нормализованным. Теперь вы можете хранить 0.1000E-99 или 1.000E-100. На всем пути до 0,0001E-99 или 1E-103, абсолютное наименьшее количество, которое вы можете сохранить.

Это вообще желательно, оно расширяет диапазон значений, которые вы можете сохранить. Что имеет значение в практических вычислениях, очень маленькие числа очень распространены в реальных проблемах, таких как дифференциальный анализ.

Однако есть и большая проблема с этим, вы теряете точность с де-нормированными числами. Точность вычислений с плавающей запятой ограничена количеством цифр, которые вы можете сохранить. Он интуитивно понятен с помощью поддельного десятичного процессора, который я использовал в качестве примера, он может вычислять только 5 значащих цифр. Пока значение нормализуется, вы всегда получаете 5 значащих цифр.

Но вы потеряете цифры при де-нормализации. Любое значение между 0.1000E-99 и 0.9999E-99 имеет только 4 значащие цифры. Любое значение между 0.0100E-99 и 0.0999E-99 имеет только 3 значащие цифры. На всем пути до 0,0001E-99 и 0,0009E-99 осталось только одна значительная цифра.

Это может значительно снизить точность результата окончательного расчета. Что еще хуже, он делает это очень непредсказуемым образом, так как эти очень маленькие ненормированные значения, как правило, проявляются в более сложных вычислениях. Это, безусловно, о чем-то беспокоиться, вы больше не можете доверять конечному результату, когда осталось только 1 значительная цифра.

Процессоры с плавающей запятой имеют способы сообщить об этом или иным образом решить проблему. Они могут, например, генерировать прерывание или сигнал, когда значение деформируется, что позволяет прерывать вычисление. И у них есть опция "flush-to-zero", бит в слове состояния, который сообщает процессору, чтобы автоматически преобразовать все нормальные значения в ноль. Который имеет тенденцию генерировать бесконечности, результат, который говорит вам, что результат является нежелательным и должен быть отброшен.

Ответ 3

Из Документация IEEE

Если показатель степени равен 0s, но фракция отлична от нуля (иначе она интерпретироваться как нуль), то это денормализованное число, который не имеет предполагаемого ведущего 1 перед двоичной точкой. Таким образом, это представляет число (-1) s × 0.f × 2-126, где s - знаковый бит, а f - дробь. Для двойной точности денормализованная числа имеют вид (-1) s × 0.f × 2-1022. Из этого вы можете интерпретировать нуль как особый тип денормализованного числа.