Чому 0,1 + 0,2 == 0,3 дюйма?


75
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

Python

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


13
Будь ласка, розмістіть відповідні приклади коду безпосередньо у питанні, а не за зовнішніми посиланнями. І для того, щоб переконатись, що повна інформація у питанні збереглася, і для полегшення читання.
Anders Abel

6
Я збирався рефлекторно натиснути кнопку закрити, поки не помітив, що ви написали ==замість !=.
dan04

2
Щодо вашого оновлення: Це не є "проблемою" з оптимізатором компілятора. Це законна поведінка з плаваючою точкою, і ймовірність цього трапляється у розділі "Постійне згортання з плаваючою точкою" документації D.
LukeH

1
Подивіться, що відбувається, коли ви використовуєте realтип замість doubleтипу: ideone.com/NAXkM
Jean Hominal

@Jean Hominal: Випадок із реальним типом цікавий. Думаючи ...
Стас,

Відповіді:


47

Ймовірно, його оптимізують до (0,3! = 0,3). Що очевидно хибно. Перевірте налаштування оптимізації, переконайтесь, що вони вимкнені, і повторіть спробу.


27
Зачекайте, чому компілятор виконує обчислення з десятковою плаваючою крапкою, а час виконання - двійкове обчислення з плаваючою точкою?
Jean Hominal

Влучне зауваження. Найцікавіше, що я щойно спробував це, і мені стає неправдою; Я не можу сам довести результат ОП. Хоча я складаю 32-біт, мені цікаво, чи має значення 64-біт.
Flynn1179

13
Це правильна відповідь. Див. Розділ " Постійне згортання з плаваючою точкою" на d-programming-language.org/float.html .
LukeH

1
Визначально щось із оптимізацією. Спробував те саме зі змінними і зрозумів: ideone.com/zO4OD
bezmax

3
Хе, я просто перечитав питання; Я думав, під "D" ви мали на увазі четвертий приклад у цьому списку; Я намагався докорити це на C #!
Flynn1179

53

(Відповідь Фліна - це правильна відповідь. Ця проблема розглядає проблему загальніше.)


Здається, ви припускаєте, ОП, що неточність з плаваючою комою у вашому коді є детермінованою та передбачуваною помилкою (певним чином, ваш підхід є полярною протилежністю підходу людей, які ще не розуміють плаваючу крапку).

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

Тут не покладайтеся ні на успіх, ні на невдачу; не покладайтесь на рівність із плаваючою точкою в будь-якому випадку .


25
Це дуже хороший момент - ви не можете покладатися на арифметику з плаваючою комою, щоб дати вам неправильну відповідь! :-)
Стів Морган,

8
Неточність з плаваючою комою НЕ дає детермінованих, передбачуваних відповідей ... до тих пір, поки ви використовуєте точки послідовності та призначення змінним, щоб змусити округлення на кожному кроці. І, остерігайтеся варіантів компілятора, які усунуть округлення, наприклад, з MSVC /fp:precise.
Ben Voigt

7
Це жахливе пояснення. IEEE 754 однозначно визначає основні операції, включаючи +. Проблема тут полягає в мові програмування, а не в плаваючій точці. Крім того, рівність з плаваючою точкою чітко визначена. Не слід використовувати його, коли це не те, що ви хочете, і все.
Паскаль Куок,

@Pascal: IEEE 754 робить. D ні. Ви стверджуєте, що "проблема тут одна мова програмування", і ... Ви маєте рацію! Якщо ви подивіться на питання дійсно уважно, ви побачите , що це позначено d, а НЕ IEEE 754. Я дуже сподіваюся, що це допоможе вам зрозуміти питання.
Гонки легкості на орбіті

@Ben: Звичайно, якщо ти контролюєш усі ці фактори. Моя відповідь передбачає, що програміст цього не робить. Я відредагував свою відповідь на слово, що краще.
Гонки легкості на орбіті

5

Згідно з моєю інтерпретацією специфікації мови D , арифметика з плаваючою комою на x86 використовувала б внутрішню точність 80 біт, а не лише 64 біти.

Проте потрібно було б перевірити, що цього достатньо, щоб пояснити результат, який ви спостерігаєте.


2
Woah, @Tomalak, моя голова просто вибухнула ;-)
Steve Morgan

2
@Tomalak: як 0,2 та 0,3 - але округлення із 80-бітною точністю замість 64 може зробити значення "рівним" замість різного. І я щойно перевірив змінні з реальним типом, і це знову оцінює як false: ideone.com/sIFgk
Жан Гомінал,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.