Почему числа с плавающей запятой неточные?


198

Почему некоторые числа теряют точность при хранении в виде чисел с плавающей запятой?

Например, десятичное число 9.2может быть выражено точно как отношение двух десятичных целых чисел ( 92/10), оба из которых могут быть выражены точно в двоичном ( 0b1011100/0b1010). Однако, то же самое отношение, сохраненное как число с плавающей запятой, никогда точно не равно 9.2:

32-bit "single precision" float: 9.19999980926513671875
64-bit "double precision" float: 9.199999999999999289457264239899814128875732421875

Как такое простое число может быть «слишком большим», чтобы его можно было выразить в 64 битах памяти?




Ответы:


242

В большинстве языков программирования числа с плавающей запятой представлены во многом подобно научной нотации : с показателем степени и мантиссой (также называемой значением). Скажем 9.2, очень простое число - это на самом деле эта дробь:

5179139571476070 * 2 -49

Где экспонента -49и мантисса 5179139571476070. Причиной невозможности представления некоторых десятичных чисел таким образом является то, что и показатель степени, и мантисса должны быть целыми числами. Другими словами, все числа с плавающей точкой должны быть целым числом, умноженным на целую степень 2 .

9.2может быть просто 92/10, но 10 не может быть выражено как 2 n, если n ограничено целочисленными значениями.


Видя данные

Сначала несколько функций, чтобы увидеть компоненты, которые делают 32- и 64-разрядные float. Если вы заботитесь только о выходных данных (например, в Python), добавьте их в глоссарий:

def float_to_bin_parts(number, bits=64):
    if bits == 32:          # single precision
        int_pack      = 'I'
        float_pack    = 'f'
        exponent_bits = 8
        mantissa_bits = 23
        exponent_bias = 127
    elif bits == 64:        # double precision. all python floats are this
        int_pack      = 'Q'
        float_pack    = 'd'
        exponent_bits = 11
        mantissa_bits = 52
        exponent_bias = 1023
    else:
        raise ValueError, 'bits argument must be 32 or 64'
    bin_iter = iter(bin(struct.unpack(int_pack, struct.pack(float_pack, number))[0])[2:].rjust(bits, '0'))
    return [''.join(islice(bin_iter, x)) for x in (1, exponent_bits, mantissa_bits)]

У этой функции много сложностей, и ее было бы довольно сложно объяснить, но если вам интересно, важным ресурсом для наших целей является модуль struct .

Python float- это 64-разрядное число с двойной точностью. В других языках, таких как C, C ++, Java и C #, двойная точность имеет отдельный тип double, который часто реализуется как 64-битный.

Когда мы вызываем эту функцию в нашем примере 9.2, вот что мы получаем:

>>> float_to_bin_parts(9.2)
['0', '10000000010', '0010011001100110011001100110011001100110011001100110']

Интерпретация данных

Вы увидите, что я разделил возвращаемое значение на три компонента. Эти компоненты:

  • Подписать
  • экспонент
  • Мантисса (также называемая значимым, или дробная)

Подписать

Знак сохраняется в первом компоненте как один бит. Это легко объяснить: 0означает, что число с плавающей точкой является положительным числом; 1означает, что это отрицательно. Потому что 9.2это положительно, наша ценность знака 0.

экспонент

Показатель степени хранится в среднем компоненте как 11 битов. В нашем случае 0b10000000010. В десятичном виде это представляет значение 1026. Причудой этого компонента является то, что вы должны вычесть число, равное 2 (число битов) - 1 - 1, чтобы получить истинный показатель степени; в нашем случае это означает вычитание 0b1111111111(десятичное число 1023) для получения истинного показателя степени 0b00000000011(десятичное число 3).

мантисса

Мантисса хранится в третьем компоненте как 52 бита. Тем не менее, есть и странность к этому компоненту. Чтобы понять эту причуду, рассмотрим число в научной нотации, например:

6.0221413x10 23

Мантисса будет 6.0221413. Напомним, что мантисса в научной нотации всегда начинается с одной ненулевой цифры. То же самое относится и к двоичному, за исключением того, что двоичный имеет только две цифры: 0и 1. Таким образом, двоичная мантисса всегда начинается с 1! При сохранении числа с плавающей точкой, 1передняя часть двоичной мантиссы опускается для экономии места; мы должны поместить его обратно в начало нашего третьего элемента, чтобы получить истинную мантиссу:

1,0010011001100110011001100110011001100110011001100110

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

Имея дело с десятичными числами, мы «перемещаем десятичную точку» путем умножения или деления на степени 10. В двоичном коде мы можем сделать то же самое, умножив или разделив на степени 2. Поскольку наш третий элемент имеет 52 бита, мы делим это на 2 52, чтобы переместить это 52 места вправо:

0,0010011001100110011001100110011001100110011001100110

В десятичной записи это то же самое, что и деление 675539944105574на, 4503599627370496чтобы получить 0.1499999999999999. (Это один пример соотношения, которое может быть выражено точно в двоичном формате, но только приблизительно в десятичном виде; более подробно см. 675539944105574/4503599627370496 .)

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

Пересмотр компонентов

  • Знак (первый компонент): 0для положительного, 1для отрицательного
  • Экспонента (средний компонент): вычтите 2 (количество бит) - 1 - 1, чтобы получить истинную экспоненту
  • Мантисса (последний компонент): разделите на 2 (количество бит) и добавьте, 1чтобы получить истинную мантиссу

Подсчет числа

Соединяя все три части вместе, мы получаем это двоичное число:

1,0010011001100110011001100110011001100110011001100110 x 10 11

Который мы можем затем преобразовать из двоичного в десятичное:

1,1499999999999999 x 2 3 (неточно!)

И умножьте, чтобы показать окончательное представление числа, с которого мы начали ( 9.2) после сохранения в виде значения с плавающей запятой:

9,1999999999999993


Представление как фракция

9,2

Теперь, когда мы построили число, можно преобразовать его в простую дробь:

1,0010011001100110011001100110011001100110011001100110 x 10 11

Смена мантиссы на целое число:

10010011001100110011001100110011001100110011001100110 x 10 11-110100

Преобразовать в десятичное число:

5179139571476070 x 2 3-52

Вычтите показатель:

5179139571476070 x 2 -49

Преврати отрицательный показатель в деление:

5179139571476070/2 49

Умножьте экспоненту:

5179139571476070/562949953421312

Что равно:

9,1999999999999993

9,5

>>> float_to_bin_parts(9.5)
['0', '10000000010', '0011000000000000000000000000000000000000000000000000']

Уже вы можете видеть, что мантисса состоит всего из 4 цифр, за которыми следует множество нулей. Но давайте пройдемся по шагам.

Соберите двоичную научную нотацию:

1,0011 x 10 11

Сместить десятичную точку:

10011 x 10 11-100

Вычтите показатель:

10011 х 10 -1

Двоичные в десятичные:

19 х 2 -1

Отрицательный показатель деления:

19/2 1

Умножьте экспоненту:

19/2

Равно:

9,5



дальнейшее чтение


1
Есть также хороший учебник, который показывает, как идти другим путем - учитывая десятичное представление числа, как вы строите эквивалент с плавающей запятой. Подход «длинного деления» очень ясно показывает, как вы получаете «остаток» после попытки представить число. Должен быть добавлен, если вы хотите быть действительно "каноническим" со своим ответом.
Флорис

1
Если вы говорите о Python и плавающей точке, я бы предложил по крайней мере включить в ваши ссылки учебник по Python: docs.python.org/3.4/tutorial/floatingpoint.html Это должно быть универсальное руководство ресурс для проблем с плавающей точкой для программистов Python. Если он каким-то образом отсутствует (и почти наверняка есть), пожалуйста, откройте проблему на трекере ошибок Python для обновлений или изменений.
Марк Дикинсон

@mhlester Если это превратится в вики сообщества, включите мой ответ в свой.
Нику Стирка

5
Этот ответ, безусловно, должен также ссылаться на плавающую точку-gui.de , так как это, вероятно, лучшее введение для начинающих. ИМО, он должен даже идти выше «Что должен знать каждый компьютерщик ...» - в наши дни люди, которые могут разумно понять статью Голдберга, обычно уже хорошо об этом знают.
Даниэль Приден

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

29

Это не полный ответ ( mhlester уже охватил много хороших вопросов, которые я не буду дублировать), но я хотел бы подчеркнуть, насколько представление числа зависит от базы, на которой вы работаете.

Рассмотрим дробь 2/3

В старом добром 10 мы обычно выписываем что-то вроде

  • 0,666 ...
  • 0,666
  • 0,667

Когда мы смотрим на эти представления, мы склонны связывать каждое из них с дробью 2/3, даже если только первое представление математически равно дроби. Второе и третье представления / приближения имеют ошибку порядка 0,001, что на самом деле намного хуже, чем ошибка между 9,2 и 9,1999999999999993. На самом деле, второе представление даже не округлено правильно! Тем не менее, у нас нет проблемы с 0,666 в качестве аппроксимации числа 2/3, поэтому у нас не должно быть проблем с приближением 9,2 в большинстве программ . (Да, в некоторых программах это имеет значение.)

Числовые базы

Так вот, где числовые базы имеют решающее значение. Если бы мы пытались представлять 2/3 в базе 3, то

(2/3) 10 = 0,2 3

Другими словами, мы имеем точное, конечное представление для того же числа путем переключения баз! Вывод состоит в том, что даже если вы можете преобразовать любое число в любое основание, все рациональные числа имеют точные конечные представления в одних основаниях, но не в других .

Чтобы обозначить эту точку зрения, давайте посмотрим на 1/2. Вас может удивить, что хотя это совершенно простое число имеет точное представление в основаниях 10 и 2, оно требует повторяющегося представления в основании 3.

(1/2) 10 = 0,5 10 = 0,1 2 = 0,1111 ... 3

Почему числа с плавающей запятой неточные?

Потому что часто они являются приближенными рациональными числами, которые нельзя представить конечным образом в базе 2 (повторение цифр), и в целом они являются приближенными действительными (возможно, иррациональными) числами, которые не могут быть представлены конечным числом цифр в любой базе.


3
Итак, другими словами, база-3 был бы идеальным для 1/3как базовой 10 идеально подходит для 1/10. Ни одна из фракций не работает в базе-2
mhlester

2
@mhlester Да. И вообще, base-N идеально подходит для любой дроби, знаменатель которой Nили кратный ей.
Нику Стирка,

2
И это одна из причин, почему некоторые числовые наборы инструментов отслеживают «что было разделено на что» и в процессе могут сохранять «бесконечную точность» для всех рациональных чисел. Точно так же, как физикам нравится сохранять свои уравнения символическими до последнего возможного момента, в случае, если факторы и πт. Д. Отменяются.
Флорис

3
@Floris Я также видел случаи, когда алгоритм, который выполняет только базовую арифметику (т.е. сохраняет рациональность ввода), определяет, был ли ввод (вероятно) рациональным, выполняет математику с использованием обычной арифметики с плавающей запятой, а затем переоценивает рациональную приближение в конце, чтобы исправить любые ошибки округления. В частности, алгоритм Matlab с уменьшенным эшелоном строк делает это, и это очень сильно помогает численной стабильности.
Нику Стирка

@SchighSchagh - интересно, я этого не знал. Я знаю, что численная стабильность - это то, чему недостаточно учат в наши дни двойной двойной точности. Это означает, что многие пропускают изучение элегантности многих прекрасных алгоритмов. Мне очень нравятся алгоритмы, которые вычисляют и исправляют свои ошибки.
Флорис

13

Хотя все остальные ответы хороши, все еще не хватает одной вещи:

Невозможно представить иррациональные числа (например , π, sqrt(2), log(3)и т.д.) точно!

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

Хотя, если вы ограничите свои математические потребности рациональными числами, только проблема точности станет управляемой. Вам нужно будет хранить пару (возможно, очень больших) целых чисел aи bсодержать число, представленное дробью a/b. Вся ваша арифметика должна делаться на дроби, как в математике в старших классах (например a/b * c/d = ac/bd).

Но, конечно , вы все равно столкнетесь с такой же проблемой , когда pi, sqrt, log, sinи т.д. участвуют.

TL; DR

Для аппаратной ускоренной арифметики может быть представлено только ограниченное количество рациональных чисел. Каждое непредставимое число аппроксимируется. Некоторые числа (то есть иррациональные) никогда не могут быть представлены независимо от системы.


4
Интересно, что иррациональные основы существуют. Финальный , например.
Veedrac

5
иррациональные числа могут быть (только) представлены в их базе. Например, пи - 10 в базе пи
phuclv

4
Точка остается в силе: некоторые числа никогда не могут быть представлены независимо от системы. Вы не получаете ничего, меняя свою базу, потому что тогда некоторые другие числа больше не могут быть представлены.
LumpN

4

Существует бесконечно много действительных чисел (так много, что вы не можете их перечислить), и существует бесконечно много рациональных чисел (их можно перечислить).

Представление с плавающей точкой является конечным (как и все, что есть в компьютере), поэтому неизбежно представить множество много-много чисел. В частности, только 64 бита позволяют различать только 18,446,744,073,709,551,616 различных значений (что ничто по сравнению с бесконечностью). Со стандартным соглашением 9.2 не является одним из них. Те, которые могут иметь форму m.2 ^ e для некоторых целых чисел m и e.


Вы можете придумать другую систему нумерации, например, на основе 10, где 9.2 будет иметь точное представление. Но другие числа, скажем 1/3, все равно невозможно представить.


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


1

Почему мы не можем представить 9.2 в двоичной с плавающей запятой?

Числа с плавающей запятой (немного упрощенно) - это система позиционной нумерации с ограниченным числом цифр и подвижной осью радиуса.

Дробь может быть выражена в точности только с помощью конечного числа цифр в системе позиционной нумерации, если главные факторы знаменателя (когда дробь выражается в ее наименьших значениях) являются факторами базы.

Первичные множители 10 равны 5 и 2, поэтому в базе 10 мы можем представить любую дробь вида a / (2 b 5 c ).

С другой стороны, единственным простым множителем 2 является 2, поэтому в базе 2 мы можем представлять только дроби вида a / (2 b )

Почему компьютеры используют это представление?

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

Конечно, было бы возможно определить формат дроби с (например) 32-разрядным числителем и 32-разрядным знаменателем. Он мог бы представлять числа, которые IEEE с плавающей запятой двойной точности не мог, но в равной степени было бы много чисел, которые могут быть представлены в плавающей запятой двойной точности, которые не могли бы быть представлены в таком формате дроби фиксированного размера.

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

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

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

Некоторые языки также предлагают десятичные типы с плавающей запятой, они в основном используются в сценариях, в которых важно, чтобы результаты, полученные компьютером, соответствовали ранее существующим правилам округления, которые были написаны с учетом интересов людей (главным образом, финансовых расчетов). С ними немного сложнее работать, чем с двоичными числами с плавающей запятой, но самая большая проблема заключается в том, что большинство компьютеров не предоставляют им аппаратную поддержку.


-4

Попробуй это

DecimalFormat decimalFormat = new DecimalFormat("#.##");
String.valueOf(decimalFormat.format(decimalValue))));

' decimalValue' - ваша ценность для конвертации.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.