9

Есть код:

public class Drob {
    public static void main(String[] args) {
        double x = 0.1;
        System.out.println(x+x);
        System.out.println(x+x+x);      // Три раза прибавили X
        System.out.println(x+x+x+x+x);  // Пять раз прибавили X
        System.out.println(x+x+x+x+x+x+x+x+x+x); //Десять раз прибавили X
    }
}

На выходе он даёт это:

0.2
0.30000000000000004
0.5
0.9999999999999999

Против первого и третьего ответа у меня возражений нет. Но откуда на хвосте 4*(10^-17) во втором ответе? И как это соотносится с -1*(10^-16) в четвертом ответе?

Nick Volynkin
  • 34,094

2 Answers2

7

Давайте зайдём с другой стороны. По стандарту IEEE 754*, число типа double хранится в виде ± n × 2k. Это значит, что при записи

double d = 0.1;

число в переменной d вовсе не будет равно 0.1, потому что 0.1 не представимо в указанном виде. Ясно, почему?** Вместо этого в d будет записано какое-то приближение к 0.1.

Поэтому и d + d + (10 раз) + d не будет в точности равно 1.

Дополнительное чтение по теме: OMG Ponies!!!


*По стандарту языка, числа с плавающей запятой соответствуют стандарту IEEE 754. (И не только в Java, кстати.)

**Потому что все числа упомянутого в стандарте IEEE 754 вида представляются в виде натуральной дроби, знаменатель которой есть степень двойки, а 1/10 таким числом не является. Действительно, если m/2n = 1/10, то 10 × m = 2n, и левая часть делится на 5, а правая нет.

VladD
  • 206,799
  • 3
    и откуда вы всё это знаете? – Andrew Kachalin Sep 22 '15 at 06:58
  • @AndrewKachalin: Нам рассказывали на лекциях. Ну и на сайте вопрос пару раз обсуждался, так что старые участники помнят. – VladD Sep 22 '15 at 09:07
3

Дело в том, что данные в компьютере хранятся в бинарной системе исчесления, а не десятичной. Поэтому, если вычисления критичные, например работа с деньгами, double или float использовать нельзя. Для работы с дробями используйте класс BigDecimal . Или округляйте, если большая точность не важна. Кстати, это не связано с языком программирования, а скорее с архитектурой процессора и памяти :)

  • 2
    Это всё-таки не объясняет наблюдаемое поведение. – Nick Volynkin Sep 21 '15 at 20:03
  • 1
    Полностью обьясняет. Вот вам более подробное обсуждение этого вопроса на английском: http://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency – Michael Vaysman Sep 21 '15 at 20:05
  • Хорошо, предположим double иногда выкидывает какой-то хвост. А как тогда хвост отбросить? Чтобы на ответ не влиял какой-то там эпсилон. – Andrew Kachalin Sep 21 '15 at 20:28
  • Ответ не полностью объясняет вопрос. Хотелось бы прояснить, как связана некоторая ε>0 в хвосте дроби с исходными данными. – Andrew Kachalin Sep 21 '15 at 20:38