Следующие несколько строк демонстрируют, что пошло не так:
double a= Math.pow(10, 16)* 4.0;
long b = (long) a;
long c = 5390195186705543L + b;
long d = (long) (5390195186705543L + 4L * Math.pow(10, 16));
long e = 5390195186705543L + 4L * (long) Math.pow(10, 16);
System.out.println("a="+a+" b="+b+" c="+c+" d = "+d+" e="+e);
Выходы:
a=4.0E16 b=40000000000000000 c=45390195186705543
d = 45390195186705544 e=45390195186705543
a , b и c имеют все ожидаемое значение, поэтому вы видите, что добавление длинных целых чисел работает как положено.
В случае d мы имеем неверный результат, потому что Math.pow(10, 16)
возвращает значение типа double, поэтому все выражение оценивается как значение типа double, а затем, наконец, преобразуется в длинное целое число. Поэтому мы добавляем здесь двойные значения, а не целые числа. Double имеет ограниченную точность, что приводит к немного неправильному результату.
Пример e дает правильный результат, потому что мы сначала конвертируем double в long перед добавлением его к другим длинным целым значениям , В этом случае мы не теряем точность, потому что 40000000000000000
может быть точно сохранен в удвоении. Но будьте осторожны, это не так хорошо работает со всеми возможными числами.
Помните, что типы данных с плавающей запятой не являются точными на 100%. Поэтому хорошее правило состоит в том, что человек никогда не должен сравнивать два числа с плавающей запятой или двойные значения на равенство. Например, более сложные выражения, чем 1.0/3.0 + 1.0/3.0 + 1.0/3.0 == 1.0
, могут потерпеть неудачу.
Если вам нужны предсказуемые результаты с определенным количеством цифр, лучше использовать BigDecimal
или BigInteger
.