Почему некоторые числа теряют точность при хранении в виде чисел с плавающей запятой?
Например, десятичное число 9.2
может быть выражено точно как отношение двух десятичных целых чисел ( 92/10
), оба из которых могут быть выражены точно в двоичном ( 0b1011100/0b1010
). Однако, то же самое отношение, сохраненное как число с плавающей запятой, никогда точно не равно 9.2
:
32-bit "single precision" float: 9.19999980926513671875
64-bit "double precision" float: 9.199999999999999289457264239899814128875732421875
Как такое простое число может быть «слишком большим», чтобы его можно было выразить в 64 битах памяти?
floating-point
language-agnostic
precision
mhlester
источник
источник
Ответы:
В большинстве языков программирования числа с плавающей запятой представлены во многом подобно научной нотации : с показателем степени и мантиссой (также называемой значением). Скажем
9.2
, очень простое число - это на самом деле эта дробь:Где экспонента
-49
и мантисса5179139571476070
. Причиной невозможности представления некоторых десятичных чисел таким образом является то, что и показатель степени, и мантисса должны быть целыми числами. Другими словами, все числа с плавающей точкой должны быть целым числом, умноженным на целую степень 2 .9.2
может быть просто92/10
, но 10 не может быть выражено как 2 n, если n ограничено целочисленными значениями.Видя данные
Сначала несколько функций, чтобы увидеть компоненты, которые делают 32- и 64-разрядные
float
. Если вы заботитесь только о выходных данных (например, в Python), добавьте их в глоссарий:У этой функции много сложностей, и ее было бы довольно сложно объяснить, но если вам интересно, важным ресурсом для наших целей является модуль struct .
Python
float
- это 64-разрядное число с двойной точностью. В других языках, таких как C, C ++, Java и C #, двойная точность имеет отдельный типdouble
, который часто реализуется как 64-битный.Когда мы вызываем эту функцию в нашем примере
9.2
, вот что мы получаем:Интерпретация данных
Вы увидите, что я разделил возвращаемое значение на три компонента. Эти компоненты:
Подписать
Знак сохраняется в первом компоненте как один бит. Это легко объяснить:
0
означает, что число с плавающей точкой является положительным числом;1
означает, что это отрицательно. Потому что9.2
это положительно, наша ценность знака0
.экспонент
Показатель степени хранится в среднем компоненте как 11 битов. В нашем случае
0b10000000010
. В десятичном виде это представляет значение1026
. Причудой этого компонента является то, что вы должны вычесть число, равное 2 (число битов) - 1 - 1, чтобы получить истинный показатель степени; в нашем случае это означает вычитание0b1111111111
(десятичное число1023
) для получения истинного показателя степени0b00000000011
(десятичное число 3).мантисса
Мантисса хранится в третьем компоненте как 52 бита. Тем не менее, есть и странность к этому компоненту. Чтобы понять эту причуду, рассмотрим число в научной нотации, например:
Мантисса будет
6.0221413
. Напомним, что мантисса в научной нотации всегда начинается с одной ненулевой цифры. То же самое относится и к двоичному, за исключением того, что двоичный имеет только две цифры:0
и1
. Таким образом, двоичная мантисса всегда начинается с1
! При сохранении числа с плавающей точкой,1
передняя часть двоичной мантиссы опускается для экономии места; мы должны поместить его обратно в начало нашего третьего элемента, чтобы получить истинную мантиссу:Это включает в себя нечто большее, чем простое сложение, потому что биты, хранящиеся в нашем третьем компоненте, на самом деле представляют дробную часть мантиссы справа от радикальной точки .
Имея дело с десятичными числами, мы «перемещаем десятичную точку» путем умножения или деления на степени 10. В двоичном коде мы можем сделать то же самое, умножив или разделив на степени 2. Поскольку наш третий элемент имеет 52 бита, мы делим это на 2 52, чтобы переместить это 52 места вправо:
В десятичной записи это то же самое, что и деление
675539944105574
на,4503599627370496
чтобы получить0.1499999999999999
. (Это один пример соотношения, которое может быть выражено точно в двоичном формате, но только приблизительно в десятичном виде; более подробно см. 675539944105574/4503599627370496 .)Теперь, когда мы превратили третий компонент в дробное число, добавление
1
дает истинную мантиссу.Пересмотр компонентов
0
для положительного,1
для отрицательного1
чтобы получить истинную мантиссуПодсчет числа
Соединяя все три части вместе, мы получаем это двоичное число:
Который мы можем затем преобразовать из двоичного в десятичное:
И умножьте, чтобы показать окончательное представление числа, с которого мы начали (
9.2
) после сохранения в виде значения с плавающей запятой:Представление как фракция
9,2
Теперь, когда мы построили число, можно преобразовать его в простую дробь:
Смена мантиссы на целое число:
Преобразовать в десятичное число:
Вычтите показатель:
Преврати отрицательный показатель в деление:
Умножьте экспоненту:
Что равно:
9,5
Уже вы можете видеть, что мантисса состоит всего из 4 цифр, за которыми следует множество нулей. Но давайте пройдемся по шагам.
Соберите двоичную научную нотацию:
Сместить десятичную точку:
Вычтите показатель:
Двоичные в десятичные:
Отрицательный показатель деления:
Умножьте экспоненту:
Равно:
дальнейшее чтение
источник
Это не полный ответ ( mhlester уже охватил много хороших вопросов, которые я не буду дублировать), но я хотел бы подчеркнуть, насколько представление числа зависит от базы, на которой вы работаете.
Рассмотрим дробь 2/3
В старом добром 10 мы обычно выписываем что-то вроде
Когда мы смотрим на эти представления, мы склонны связывать каждое из них с дробью 2/3, даже если только первое представление математически равно дроби. Второе и третье представления / приближения имеют ошибку порядка 0,001, что на самом деле намного хуже, чем ошибка между 9,2 и 9,1999999999999993. На самом деле, второе представление даже не округлено правильно! Тем не менее, у нас нет проблемы с 0,666 в качестве аппроксимации числа 2/3, поэтому у нас не должно быть проблем с приближением 9,2 в большинстве программ . (Да, в некоторых программах это имеет значение.)
Числовые базы
Так вот, где числовые базы имеют решающее значение. Если бы мы пытались представлять 2/3 в базе 3, то
Другими словами, мы имеем точное, конечное представление для того же числа путем переключения баз! Вывод состоит в том, что даже если вы можете преобразовать любое число в любое основание, все рациональные числа имеют точные конечные представления в одних основаниях, но не в других .
Чтобы обозначить эту точку зрения, давайте посмотрим на 1/2. Вас может удивить, что хотя это совершенно простое число имеет точное представление в основаниях 10 и 2, оно требует повторяющегося представления в основании 3.
Почему числа с плавающей запятой неточные?
Потому что часто они являются приближенными рациональными числами, которые нельзя представить конечным образом в базе 2 (повторение цифр), и в целом они являются приближенными действительными (возможно, иррациональными) числами, которые не могут быть представлены конечным числом цифр в любой базе.
источник
1/3
как базовой 10 идеально подходит для1/10
. Ни одна из фракций не работает в базе-2N
или кратный ей.π
т. Д. Отменяются.Хотя все остальные ответы хороши, все еще не хватает одной вещи:
Невозможно представить иррациональные числа (например , π,
sqrt(2)
,log(3)
и т.д.) точно!И именно поэтому они называются иррациональными. Никакого количества битового хранилища в мире не хватит, чтобы вместить даже один из них. Только символическая арифметика способна сохранить их точность.
Хотя, если вы ограничите свои математические потребности рациональными числами, только проблема точности станет управляемой. Вам нужно будет хранить пару (возможно, очень больших) целых чисел
a
иb
содержать число, представленное дробьюa/b
. Вся ваша арифметика должна делаться на дроби, как в математике в старших классах (напримерa/b * c/d = ac/bd
).Но, конечно , вы все равно столкнетесь с такой же проблемой , когда
pi
,sqrt
,log
,sin
и т.д. участвуют.TL; DR
Для аппаратной ускоренной арифметики может быть представлено только ограниченное количество рациональных чисел. Каждое непредставимое число аппроксимируется. Некоторые числа (то есть иррациональные) никогда не могут быть представлены независимо от системы.
источник
Существует бесконечно много действительных чисел (так много, что вы не можете их перечислить), и существует бесконечно много рациональных чисел (их можно перечислить).
Представление с плавающей точкой является конечным (как и все, что есть в компьютере), поэтому неизбежно представить множество много-много чисел. В частности, только 64 бита позволяют различать только 18,446,744,073,709,551,616 различных значений (что ничто по сравнению с бесконечностью). Со стандартным соглашением 9.2 не является одним из них. Те, которые могут иметь форму m.2 ^ e для некоторых целых чисел m и e.
Вы можете придумать другую систему нумерации, например, на основе 10, где 9.2 будет иметь точное представление. Но другие числа, скажем 1/3, все равно невозможно представить.
Также обратите внимание, что числа с плавающей запятой двойной точности чрезвычайно точны. Они могут представлять любое число в очень широком диапазоне с 15 точными цифрами. Для ежедневных вычислений 4 или 5 цифр более чем достаточно. Вам никогда не понадобятся эти 15, если вы не хотите считать каждую миллисекунду своей жизни.
источник
Числа с плавающей запятой (немного упрощенно) - это система позиционной нумерации с ограниченным числом цифр и подвижной осью радиуса.
Дробь может быть выражена в точности только с помощью конечного числа цифр в системе позиционной нумерации, если главные факторы знаменателя (когда дробь выражается в ее наименьших значениях) являются факторами базы.
Первичные множители 10 равны 5 и 2, поэтому в базе 10 мы можем представить любую дробь вида a / (2 b 5 c ).
С другой стороны, единственным простым множителем 2 является 2, поэтому в базе 2 мы можем представлять только дроби вида a / (2 b )
Потому что это простой формат для работы и он достаточно точен для большинства целей. По сути, по той же причине ученые используют «научные обозначения» и округляют свои результаты до разумного числа цифр на каждом шаге.
Конечно, было бы возможно определить формат дроби с (например) 32-разрядным числителем и 32-разрядным знаменателем. Он мог бы представлять числа, которые IEEE с плавающей запятой двойной точности не мог, но в равной степени было бы много чисел, которые могут быть представлены в плавающей запятой двойной точности, которые не могли бы быть представлены в таком формате дроби фиксированного размера.
Однако большая проблема заключается в том, что такой формат является трудной задачей для расчетов. По двум причинам.
Некоторые языки предлагают типы дробей, но обычно они делают это в сочетании с произвольной точностью, это избавляет от необходимости беспокоиться об аппроксимации дробей, но создает свою собственную проблему, когда число проходит через большое количество шагов вычисления, размер знаменателя и следовательно, хранилище, необходимое для фракции, может взорваться.
Некоторые языки также предлагают десятичные типы с плавающей запятой, они в основном используются в сценариях, в которых важно, чтобы результаты, полученные компьютером, соответствовали ранее существующим правилам округления, которые были написаны с учетом интересов людей (главным образом, финансовых расчетов). С ними немного сложнее работать, чем с двоичными числами с плавающей запятой, но самая большая проблема заключается в том, что большинство компьютеров не предоставляют им аппаратную поддержку.
источник
Попробуй это
'
decimalValue
' - ваша ценность для конвертации.источник