Почему 0,1 + 0,2 == 0,3 в D?

assert(0.1 + 0.2 != 0.3); // shall be true

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

C++

#include <cstdio>

int main()
{
   printf("%d\n", (0.1 + 0.2 != 0.3));
   return 0;
}

Вывод:

1

http://ideone.com/ErBMd

питон

print(0.1 + 0.2 != 0.3)

Вывод:

True

http://ideone.com/TuKsd

Другие примеры

Почему это не относится к D? Как вы понимаете, D использует собственные числа с плавающей запятой. Это ошибка? Используют ли они какое-то конкретное числовое представление? Что-то другое? Довольно запутанно.

D

import std.stdio;

void main()
{
   writeln(0.1 + 0.2 != 0.3);
}

Вывод:

false

http://ideone.com/mX6zF


ОБНОВИТЬ

Спасибо LukeH. Это эффект сворачивания констант с плавающей запятой, описанный здесь.

Код:

import std.stdio;

void main()
{
   writeln(0.1 + 0.2 != 0.3); // constant folding is done in real precision

   auto a = 0.1;
   auto b = 0.2;
   writeln(a + b != 0.3);     // standard calculation in double precision
}

Вывод:

false
true

http://ideone.com/z6ZLk


person Stas    schedule 29.07.2011    source источник
comment
Пожалуйста, размещайте соответствующие примеры кода непосредственно в вопросе, а не по внешним ссылкам. Как для того, чтобы убедиться, что полная информация в вопросе сохранилась, так и для того, чтобы ее было легче читать.   -  person Anders Abel    schedule 29.07.2011
comment
Я собирался рефлекторно нажать кнопку закрытия, пока не заметил, что вы написали == вместо !=.   -  person dan04    schedule 29.07.2011
comment
Что касается вашего обновления: это не проблема с оптимизатором компилятора. Это разрешенное поведение с плавающей запятой, и возможность этого объясняется в Константа с плавающей запятой. Складной раздел документации D.   -  person LukeH    schedule 29.07.2011
comment
Посмотрите, что происходит, когда вы используете тип real вместо типа double: ideone.com/NAXkM   -  person Jean Hominal    schedule 29.07.2011
comment
@Jean Hominal: Случай с реальным шрифтом интересен. Думая...   -  person Stas    schedule 29.07.2011
comment
@Anders Abel: добавлены примеры кода, но только для C++ и Python. Java и С# слишком многословны, имхо :)   -  person Stas    schedule 01.08.2011
comment
У Computerphile есть отличное видео, объясняющее плавающие точки youtube.com/watch?v=PZRI1IfStY0.   -  person Felipe Sabino    schedule 01.06.2014
comment
Это происходит и в Ruby   -  person Furkan Ayhan    schedule 01.06.2014


Ответы (3)


Вероятно, он оптимизируется до (0,3!= 0,3). Что явно неверно. Проверьте настройки оптимизации, убедитесь, что они отключены, и повторите попытку.

person Flynn1179    schedule 29.07.2011
comment
Подождите, почему компилятор должен выполнять десятичные вычисления с плавающей запятой, а среда выполнения - двоичные вычисления с плавающей запятой? - person Jean Hominal; 29.07.2011
comment
Хорошая точка зрения. Самое смешное, что я только что попробовал это, и я ошибся; Я не могу воспроизвести результат ОП сам. Однако я компилирую в 32-битную версию, мне интересно, имеет ли значение 64-битная версия. - person Flynn1179; 29.07.2011
comment
Это правильный ответ. См. раздел «Свертывание констант с плавающей запятой» в d-programming-language.org/float.html< /а>. - person LukeH; 29.07.2011
comment
Что ж, я только что попытался установить 0.1f + 0.2f != 0.3f, и это действительно верно. - person Flynn1179; 29.07.2011
comment
Из ссылки, которую я разместил в своем комментарии: различные настройки компилятора, настройки оптимизации, а настройки встраивания могут повлиять на возможности свертывания констант, поэтому результаты вычислений с плавающей запятой могут отличаться в зависимости от этих настроек. - person LukeH; 29.07.2011
comment
Хех, я только что перечитал вопрос; Я подумал, что под «D» вы имели в виду четвертый пример в этом списке; Я пытался воспроизвести это на С#! - person Flynn1179; 29.07.2011
comment
Интересно, что IEEE 754 имеет новый тип данных, называемый decimal32 (и decimal64). Большинство людей думают о плавающей запятой как о binary32 и binary64, как определено спецификацией. Полезные типы (которые многие люди подразумевают под плавающей запятой) на самом деле decimal64. Позволяет ли язык D указать, является ли число с плавающей запятой двоичным или десятичным, и включает ли он это для констант? Использование 0.2 в языке без какого-либо спецификатора типа для константы кажется частью проблемы. - person Brian Bulkowski; 14.11.2015

(Ответ Флинна является правильным ответом. Этот ответ решает проблему в более общем плане.)


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

Хотя (как указывает Бен) неточность с плавающей запятой является детерминированной, с точки зрения вашего кода, если вы не очень тщательно следите за тем, что происходит с вашими значениями на каждом шаге, это не будет быть дело. Любое количество факторов может привести к успеху 0.1 + 0.2 == 0.3, одним из которых является оптимизация времени компиляции, а другим — измененные значения для этих литералов.

Не полагайтесь здесь ни ни на успех, ни на неудачу; не полагайтесь на равенство с плавающей запятой в любом случае.

person Lightness Races in Orbit    schedule 29.07.2011
comment
Это очень хороший момент — вы не можете полагаться на арифметику с плавающей запятой, чтобы получить неправильный ответ! :-) - person Steve Morgan; 29.07.2011
comment
Неточность с плавающей запятой ДЕЙСТВИТЕЛЬНО дает детерминированные, предсказуемые ответы ... до тех пор, пока вы используете точки последовательности и присваивания переменным для принудительного округления на каждом шаге. И остерегайтесь параметров компилятора, которые устранят округление, например, с MSVC следует использовать /fp:precise. - person Ben Voigt; 29.07.2011
comment
Это ужасное объяснение. IEEE 754 однозначно определяет базовые операции, включая +. Проблема здесь в языке программирования, а не в плавающей запятой. Кроме того, равенство с плавающей запятой прекрасно определено. Вы не должны использовать его, когда это не то, что вам нужно, вот и все. - person Pascal Cuoq; 30.07.2011
comment
@Pascal: IEEE 754 делает. Д нет. Вы утверждаете, что проблема здесь в одном языке программирования, и... вы правы! Если вы внимательно посмотрите на вопрос действительно, вы увидите, что он помечен d, а не IEEE 754. Я очень надеюсь, что это поможет вам понять вопрос. - person Lightness Races in Orbit; 01.08.2011
comment
@Бен: Конечно, если ты контролируешь все эти факторы. Мой ответ предполагает, что программист этого не делает. Я отредактировал свой ответ, чтобы слово было лучше. - person Lightness Races in Orbit; 01.08.2011

Согласно моей интерпретации спецификации языка D, арифметика с плавающей запятой на x86 будет использовать 80-битная внутренняя точность вместо 64-битной.

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

person Jean Hominal    schedule 29.07.2011
comment
Вау, @Tomalak, у меня просто голова взорвалась ;-) - person Steve Morgan; 29.07.2011
comment
@Tomalak: как и 0,2 и 0,3, но округление с 80-битной точностью вместо 64 может сделать значение равным, а не различным. И я только что проверил переменные с реальным типом, и он снова оценивается как false: ideone.com/sIFgk - person Jean Hominal; 29.07.2011