Почему Visual Studio 2008 сообщает мне, что .9 - .8999999999999995 = 0,00000000000000055511151231257827?

Когда я набираю это в непосредственном окне Visual Studio 2008:

? .9 - .8999999999999995

Это дает мне это как ответ:

0.00000000000000055511151231257827

В документации сказано, что двойник имеет точность 15-16 цифр, но он дает мне результат с точностью 32 цифры. Откуда берется вся эта дополнительная точность?


person raven    schedule 02.11.2009    source источник
comment
Интересно, сколько вопросов, связанных с проблемами точности с плавающей запятой, у нас есть на SO.   -  person mmx    schedule 02.11.2009
comment
@Mehrdad: Слишком много. Это такая простая вещь: основание 10 != основание 2. Тем не менее, все чувствуют себя обязанными относиться к этому, как к самой удивительной вещи на свете!   -  person S.Lott    schedule 02.11.2009
comment
Люди, пожалуйста, прочитайте, это о том, почему в ответе используется 32 цифры. Не о том, чтобы быть точным.   -  person Henk Holterman    schedule 02.11.2009
comment
Хенк, пожалуйста, пойми, это не то, как работает с плавающей запятой.   -  person Jimbo    schedule 02.11.2009
comment
Хенк, я думаю, что этот вопрос можно было бы сформулировать лучше. Если бы ОП даже упомянул в начале вопроса, что он / она знает о точности FP, то это, вероятно, остановило бы все ответы FP.   -  person Jim Deville    schedule 02.11.2009
comment
Комментарий @Mehrdad заставляет меня задаться вопросом, сколько различных проблем с точностью с плавающей запятой возможно.   -  person Bill the Lizard    schedule 02.11.2009
comment
@Mehrdad: я пишу скрипт для подсчета, и таких вопросов 201.00000001918371. не уверен, как я посчитал дробный вопрос, может быть, я опубликую вопрос с моим исходным кодом, чтобы попросить объяснения.   -  person toasteroven    schedule 02.11.2009


Ответы (6)


В ответе есть всего 15-16 цифр. Все эти ведущие нули не учитываются. На самом деле число больше похоже на 5,5511151231257827 10-16. Часть мантиссы состоит из 15-16 цифр. Показатель степени (-16) служит для сдвига десятичной точки на 16 знаков, но не меняет количество цифр в общем числе.

Редактировать

После того, как я получил несколько комментариев, мне стало любопытно, что происходит на самом деле. Я вставил этот номер в этот преобразователь IEEE-754. Потребовалось смелость округлить последние «27» до «30», но я не думаю, что это меняет результаты.

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

Знак: 0 (положительный)
Показатель степени: -51
Значимость: 1,01000000000000000000000000000000000000000000000000000000 (двоичный код для 1,2510)

Таким образом, это число равно 1,012 2-51 или 1,2510 2-51. Поскольку хранится только три значащих двоичных разряда, можно предположить, что Ларс что-то понял. Они не могут быть «случайным шумом», поскольку они одинаковы при каждом преобразовании числа.

Данные предполагают, что единственная сохраненная цифра — «5». Начальные нули взяты из экспоненты, а остальные кажущиеся случайными цифры получены из вычисления 2-51.

person Barry Brown    schedule 02.11.2009
comment
Где хранятся все эти другие цифры? - person Barry Brown; 03.11.2009
comment
Нет, это десятичное представление двоичного числа точек. В том числе и ошибки округления. - person Dykam; 03.11.2009
comment
Неправильно: ведущие нули не учитываются. 15-16 важных цифр в ответе: 0,0000000000000005. Цифры после этого неверны и присутствуют только потому, что ошибки округления превращаются в мантиссу из-за начальных нулей. Как написал сам Барри в своем редактировании, результат сохраняется как 1,25 * 2 ^ (-51) = 5,551 * 10 ^ (-16), что является числом, которое имеет 1 десятичную цифру точности вычисления, но имеет 15-16 цифры точности хранения. - person Lars D; 03.11.2009
comment
Более интересное поведение появляется с Convert.ToSingle("9111111.4999999990") и convert.ToSingle("9111111.4999999991"). Оба значения ближе к 9111111, чем к 9111112, но последнее округляется в большую сторону. - person supercat; 27.10.2012

Вам следует прочитать: Что должен знать каждый программист об арифметике с плавающей запятой.

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

if(.9 - .8999999999999995 <= 0.0001)
  //close enough to be equal
person Lolindrath    schedule 02.11.2009
comment
+1: Жаль, что SO не может автоматически отвечать на это для каждого вопроса с плавающей запятой, что является правильным ответом как минимум на 90% вопросов с плавающей запятой. - person Richard; 02.11.2009
comment
это не касается его вопроса о точности. - person Ape-inago; 02.11.2009
comment
-1, не отвечает на вопрос. q составляет около 32 цифр (не 16) для двойного числа. - person Henk Holterman; 02.11.2009
comment
+1 Количество цифр на самом деле не имеет значения, это метод хранения, к которому относится этот ответ. - person StackOverflowed; 19.10.2012

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

Часть 55511151231257827 (которая является мантисса или мантисса) состоит из 17 десятичных цифр, что достаточно близко к 15-16 цифрам.

@Lars D: То, что вы считаете правильным, правильно только в контексте вопроса. .9 - .8999999999999995 работает с числом с плавающей запятой с значащей величиной 0,625 и показателем степени -50. Взяв 0,625 * 2-50, получим 5,5511151231257827e-16. Теперь, вне контекста исходного вопроса, у нас есть число с 17 значащими цифрами, которое действительно оказывается нашим лучшим двоичным приближением 0,00000000000000005. Однако эти начальные нули по-прежнему не имеют значения с точки зрения представления числа с плавающей запятой.

person Mark Rushakoff    schedule 02.11.2009
comment
Я проголосовал за этот ответ, потому что считаю ваш ответ неверным. Точность 15-16 цифр в фактической операции вычитания дает 0,00000000000000005, а остальные цифры являются случайными ошибками округления. Следовательно, в части 55511151231257827 только 1 правильная цифра, а остальные — ошибки округления, которые из-за начальных нулей превратились в мантиссу. По сути, 32 цифры — это 16 значащих цифр от вычитания плюс 16 цифр шума в мантиссе впоследствии. Нули становятся незначимыми только ПОСЛЕ, когда результат сохраняется. - person Lars D; 03.11.2009
comment
Правильно - если бы я снова изучал химию, ведущие нули были бы значащими цифрами. Однако компьютер понятия не имеет, должны ли нули быть значащими, поэтому в ответе представлено примерно 16 ненулевых цифр. - person Mark Rushakoff; 03.11.2009

? .9 - .8999999999999995

Этот процесс вычитания с 15-16 значащими цифрами дает

0.0000000000000005

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

person Lars D    schedule 02.11.2009

«Плавающая» часть «плавающей запятой» означает, что вы приближаетесь к 5,5511151231257827 * 10^(-16). Это не совсем то, как это представлено, потому что, конечно, все это делается в двоичном виде под капотом, но дело в том, что число представлено значащими цифрами плюс число, которое представляет, как далеко сдвинуть систему счисления (десятичную точку). Как всегда, википедия может дать вам более подробную информацию:

(Вторая ссылка более конкретно ориентирована на ваш конкретный случай.)

person jcdyer    schedule 02.11.2009

Я думаю, это потому, что в двоичной системе число 5 является периодическим, поскольку оно не делится на 2. И тогда применимо то, что сказал Марк Рушаков.

person eWolf    schedule 02.11.2009