Ответ 1
Все эти языки используют системный формат с плавающей запятой, который представляет значения в двоичном, а не в десятичном формате. Значения типа 0.2
и 0.4
не могут быть представлены точно в этом формате, поэтому вместо этого сохраняется самое близкое представляемое значение, что приводит к небольшой ошибке. Например, числовой литерал 0.2
приводит к числу с плавающей запятой, точное значение которого 0.200000000000000011102230246251565404236316680908203125
. Точно так же любая заданная арифметическая операция с числами с плавающей запятой может привести к тому, что значение не будет точно отображаться, поэтому истинный математический результат заменяется ближайшим представимым значением. Это основные причины ошибок, которые вы видите.
Однако это не объясняет различия между языками: во всех ваших примерах выполняются одни и те же вычисления и получены точные результаты. Разница заключается в том, что различные языки предпочитают отображать результаты.
Строго говоря, ни один из ответов, которые вы показываете, не является правильным. Выполняя (достаточно безопасное) предположение о бинарной арифметике IEEE 754 с алгоритмом округления до ближайшего округления, точное значение первой суммы:
0.600000000000000088817841970012523233890533447265625
тогда как точное значение второй суммы:
0.59999999999999997779553950749686919152736663818359375
Однако ни один из этих выходов не является особенно удобным для пользователя, и, очевидно, все языки, на которые вы протестировали, приняли разумное решение сократить время печати при печати. Тем не менее, они не все используют одну и ту же стратегию для форматирования вывода, поэтому вы видите различия.
Существует много возможных стратегий форматирования, но три наиболее распространенных:
-
Вычислить и отобразить 17 правильно округленных значащих цифр, возможно, зачищая конечные нули, где они появляются. Выход из 17 цифр гарантирует, что отдельные бинарные64-поплавки будут иметь разные представления, так что значение с плавающей запятой можно однозначно восстановить из своего представления; 17 - наименьшее целое с этим свойством. Это стратегия, которую использует, например, Python 2.6.
-
Вычислить и отобразить кратчайшую десятичную строку, которая округляется до заданного значения binary64 в обычном режиме округления до четного округления. Это сложнее реализовать, чем стратегия 1, но сохраняет свойство, что отдельные поплавки имеют различные представления и, как правило, делают для получения удовольствия. Это, по-видимому, стратегия, в которой используются все языки, которые вы тестировали (помимо R).
-
Вычислить и отобразить 15 (или меньше) правильно округленных значащих цифр. Это приводит к скрытию ошибок, связанных с преобразованием десятичных чисел в двоичные числа, что дает иллюзию точной десятичной арифметики. У этого есть недостаток, что отдельные поплавки могут иметь одинаковое представление. Кажется, это то, что делает R. (Спасибо @hadley за указание в комментариях, что существует R настройка, которая управляет количеством цифр, используемых для отображения, по умолчанию используется 7 значащих цифр.)