19 апр. 2015 г.

Примитивные вещественные типы - знакомство с граблями

GrabliПри работе с вещественными типами в Java есть несколько граблей, часть из них, не самые страшные, уже были упомянуты в предыдущей статье.

Теперь рассмотрим несколько других, наиболее интересных :) Которые не часто освещаются в книга по программированию на Java и соответственно часто вызывают недоумения у начинающих программистов и не только на Java, поскольку это связано с представлением чисел с плавающей точкой в процессорах.

Чтобы сразу стало понятно о чём пойдет речь, разберем простой пример. Как вы уже должны знать, вещественные литералы в Java, по умолчанию имеют тип double. Теперь допустим у нас есть вот такая строка:

double d = 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1;

То есть мы 10 раз складываем число 0.1. Можно предположить что в результате получится 1, но как бы не так. Мы получим число очень близкое к единице но не единицу, например это может быть число:

0.9999999999999999

Если же мы этот же эксперимент проделаем для типа float:

float f = 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f;

то там будет другой результат:

1.0000001

Это объясняется разностью в точности представления вещественных чисел в типах double и float в двоичном формате (вспоминаем, что компьютер работает только с нулями и единицами :) ).

Более подробно и популярно почему так происходит можно почитать тут и тут. Чистая теори о представлении вещественных чисел тут.

Из этого следует, что вещественные числа обычно не сравнивают оператором ==, а проверяют что модуль разности двух чисел меньше какого-то маленького числа (погрешности вычислений).

Еще одними грабельками при вычислениях с вещественными числами может быть ситуация когда возможно подобрать такое положительное число Х при котором будет верно сравнение a+x == x, то есть прибавление какого-то числа к переменной не меняет ее значения.

Чтобы стало все понятнее приведу пример:

Вывод программы:

FP0002

FP0003Приведу еще один интересный пример слева и его вывод ниже:

FP0004Как видим, в первом случае dd и ff не равны друг другу. Такое происходит при сужающем преобразовании.

Затем мы сделали наоборот и теперь dd равно ff. Весело, не правда ли? :)

 

Именно из-за такого поведения double и float не используют в финансовых расчётах.

И еще парочка статей на эту тему: раз и два.

Ну и на последок, очень хорошая статья на эту тему. А BigInteger и BigDecimal, рассматриваемые в ней, мы изучим чуть позже.

Комментариев нет:

Отправить комментарий