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

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

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

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

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

4 ответа

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

5179139571476070 * 2-49

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

9.2 может быть просто 92/10, но 10 нельзя выразить как 2n, если 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.

Питона 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.0221413x1023

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

1,0010011001100110011001100110011001100110011001100110

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

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

0,0010011001100110011001100110011001100110011001100110

В десятичной записи это то же самое, что деление 675539944105574 от 4503599627370496 получить 0.1499999999999999, (Это один из примеров соотношения, которое может быть выражено точно в двоичном формате, но только приблизительно в десятичном виде; более подробно см. http://www.wolframalpha.com/input/?i=675539944105574+%2F+4503599627370496.)

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

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

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

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

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

1,0010011001100110011001100110011001100110011001100110 x 1011

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

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

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

9,1999999999999993


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

9,2

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

1,0010011001100110011001100110011001100110011001100110 x 1011

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

10010011001100110011001100110011001100110011001100110 x 1011-110100

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

5179139571476070 x 23-52

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

5179139571476070 x 2-49

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

5179139571476070/249

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

5179139571476070/562949953421312

Что равно:

9,1999999999999993

9,5

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

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

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

1,0011 x 1011

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

10011 x 1011-100

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

10011 х 10-1

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

19 х 2-1

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

19/21

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

19/2

Равно:

9,5



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

Это не полный ответ ( 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,23

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

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

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

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

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

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

Невозможно представить иррациональные числа (например, π, 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, если вы не хотите считать каждую миллисекунду своей жизни.

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

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

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

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

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

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

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

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

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

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

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

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

Попробуй это

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

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

Другие вопросы по тегам