Какие типы чисел представляются в двоичной с плавающей запятой?
Я много читал о поплавках, но все это излишне. Я думаю, что я понял это довольно хорошо, но я хотел бы точно знать только одну вещь:
Я знаю, что фракции формы 1/pow(2,n)
, с n
целым числом, могут быть представлены точно в числах с плавающей запятой. Это означает, что если я добавлю 1/32
к себе 32 миллиона раз, я бы получил ровно 1,000,000
.
Как насчет чего-то вроде 1/(32+16)
? Это одно над суммой двух степеней два, это работает? Или это работает 1/32+1/16
? Вот где я в замешательстве, поэтому, если кто-нибудь сможет прояснить это, я буду признателен.
Ответы
Ответ 1
Правило можно суммировать следующим образом:
- Число может быть представлено точно в двоичном выражении, если простая факторизация знаменателя содержит только 2. (т.е. знаменатель является степенью двух)
Итак, 1/(32 + 16)
не представляется в двоичном виде, потому что он имеет коэффициент 3 в знаменателе. Но 1/32 + 1/16 = 3/32
есть.
Тем не менее, существует больше ограничений для представления в типе с плавающей точкой. Например, у вас есть только 53 бит мантиссы в IEEE double
, поэтому 1/2 + 1/2^500
не может быть представлен.
Таким образом, вы можете делать сумму степеней-двух, если диапазон экспонентов не превышает более 53 степеней.
Чтобы обобщить это на другие базы:
-
Число может быть точно представлено в базе 10, если простая факторизация знаменателя состоит только из 2 и 5.
-
Рациональное число X
может быть точно представлено в базе N
, если простая факторизация знаменателя X
содержит только простые числа, найденные при факторизации N
.
Ответ 2
Конечное число может быть представлено в общем формате двойной точности IEEE 754 тогда и только тогда, когда оно равно M • 2 e для некоторых целых чисел M и e таких, что -2 53 M < 2 53 и -1074 ≤ e ≤ 971.
Для одиночной точности -2 24 M < 2 24 и -149 ≤ e ≤ 104.
Для двойной точности это последствия фактов, что формат двойной точности использует 52 бита для хранения значимости (которая обычно имеет 53 бита из-за неявного 1) и использует 11 бит для хранения экспоненты. 11 бит кодирует числа от 0 до 2047, но 0 и 2047 исключены для специальных целей, а кодированный номер смещен на 1023, поэтому он представляет собой несмещенные показатели от -1022 до 1023. Однако эти несмещенные показатели являются значащими в интервале [1, 2], и эти значения имеют фракции. Чтобы выразить значение как целое число, я скорректировал диапазон экспоненты на 52. Одинарная точность аналогична, с 23 битами для хранения 24-битного значения, 8 бит для экспоненты и смещения 127.
Выражая представляемые числа, используя целое число раз, мощь двух, а не более общее дробное значение, упрощает некоторую теорию чисел и другие рассуждения о свойствах с плавающей запятой. Я использовал его в этом ответе, потому что он позволяет кратко выразить набор представляемых значений.
Ответ 3
Числа с плавающей запятой буквально представлены с помощью формы:
1.m * 2^e
Где 1.m
- двоичная дробь, а e
- положительное или отрицательное целое число.
Таким образом, вы можете точно представить 1/32 + 1/16
, как:
1.1000000 * 2^-4
(1.10
является двоичной дробью, эквивалентной 1.5.) 1/48
, однако, не представляется в этом формате.
Ответ 4
Одна точка, еще не упомянутая, состоит в том, что семантически число с плавающей запятой лучше всего рассматривать как представляющее диапазон значений. Диапазон значений имеет очень точно определенную центральную точку, и спецификация IEEE обычно требует, чтобы результатом вычисления с плавающей запятой было число, диапазон которого содержит точку один, будет работать по центральным точкам исходных чисел, но в последовательности:
double N1 = 0.1;
float N2 = (float)N1;
double N3 = N2;
N2 - однозначное правильное представление с единственной точностью значения, которое было представлено в N1, несмотря на глупое требование использования явного приведения. N3 будет представлять одно из значений, которое может представлять N2 (спецификация языка выбирает значение double
, диапазон которого находится по центру в середине диапазона float
). Обратите внимание, что хотя N2 представляет значение своего типа, диапазон которого содержит правильное значение, N3 не делает.
Кстати, преобразование числа из строки в float в .net и .net-языках, кажется, проходит промежуточное преобразование в double
, что иногда может изменить значение. Например, хотя значение 13571357 представляется как float с одной точностью, значение 13571357.499999999069f округляется до 13571358 (хотя оно явно приближается к 13571357).