Главная » Java, Советы » Если требуются точные ответы, избегайте использования типов float и doubIe

0

 

Типы float и double в первую очередь предназначены для научных и инженерных расчетов. Oни реализуют бинарную арифметику с плавающей точкой (binary f!oating-point arithmetic), которая была тщательно выстроена с тем, чтобы быстро получать правильное приближение для широкого диапазона значений. Однако эти типы не дают точного результата, и в ряде случаев их нельзя использовать. Типы float и double не подходят для денежных расчетов, поскольку с их помощью невозможно представить число 0.1 (или любую другую отрицательную степень числа десять).

Например, у вас в кармане лежит $1.03, и вы тратите 42 цента. Сколько денег у вас осталось? Приведем фрагмент наивной программы, которая пытается ответить на этот вопрос:

 

Sуstеm.оut.рrintln(1.0З – .42);

 

Как ни печально, программа выводит 0.6100000000000001. И это не единственный случай. Предположим, что у вас в кармане есть доллар, и вы покупаете девять прокладок для крана по десять центов за каждую. Какую сдачу вы получите?

 

System.out.println(1.00 – 9*.10);

 

Если верить этому фрагменту программы, то вы получите $0.09999999999999995. Может быть, проблему можно решить, округлив результаты перед печатью? К сожалению, это срабатывает не всегда. Например, у вас в кармане есть доллар, и вы видите

 

полку, где выстроены в ряд вкусные конфеты за 10, 20, 30 центов и так далее вплоть до доллара. Вы покупаете по одной конфете каждого вида, начиная с той, что стоит 10 центов, и так далее, пока у вас еще есть возможность взять следующую конфету. Сколько конфет вы купите и сколько получите сдачи? Решим эту задачу следующим образом:

 

// Ошибка: использование плавающей точки для денежных расчетов!

public static void main(String[] args) {

double funds = 1.00;

int itemsBought = 0;

for (double price = .10; funds >= price; price += .10) {

funds -= price;

itemsBought++; }

System.out.println(itemsBought + " items bought."); System.out.println("Change: $" + funds);   }

Запустив программу, вы выясните, что можете позволить себе .три конфеты и у вас останется еще $0.39999999999999999. Но это неправильный ответ! Правильный путь решения задачи заключается в применении для денежных расчетов типов BigDecimal, int или long. Представ м простое преобразование предыдущей программы, которое позволяет использовать тип BigDecimal вместо double:

public static void main(String[] args) {

final BigDecimal TEN_CENTS = new BigDecimal( ".10”);

int itemsBought = 0;

BigDecimal funds = new BigDecimal("1.00");

for (BigDecimal price = TEN_CENTS;

funds.compareTo(price) >= 0;

price = price.add(TEN_CENTS)) {

itemsBought++;

funds = funds.subtract(price);   }

System.out.print1n(itemsBought + " items bought."); System.out.print1n("Money left over: $" + funds);  }

Запустив исправленную программу, вы обнаружите, что можете позволить себе четыре конфеты и у вас останется $0.00. Это верный ответ. Однако тип BigDecimal имеет два недостатка: он не столь удобен и медленнее, чем простой арифметический тип. Последнее можно считать несущественным, если вы решаете единственную маленькую задачу, а вот неудобство может раздражать.

 

Вместо BigDecimal можно использовать int или long (в зависимости от обрабатываемых величин) и самостоятельно отслеживать положение десятичной точки. В нашем при мере расчеты лучше производить не в долларах, а в центах. Продемонстрируем этот подход:

 

public static void main(String[] args) {

 int itemsBought = 0;

int funds = 100;

for (int price = 10; funds >= price; price += 10) {

itemsBought++;

funds – = price;

}

System.out.println(itemsBought + " items bought.");

System.out.println("Money left over: " + funds + " cents"); }

 

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

Источник: Джошуа Блох, Java TM Эффективное программирование, Издательство «Лори»

По теме:

  • Комментарии