Как проверить, сохранено ли значение в двойном вставке? (округление да, но усечение нет)

Я думаю, что вопрос довольно прямой. но вот примеры.

Пример ниже - ОК. Я могу округлить, и здесь не было никаких усечений.

public static void main(String[] args) {
 ****** d = 9.9;
 long l = (long)d;
 System.out.println(l);
}

Вывод:

9

И теперь число вне диапазона long:

public static void main(String[] args) {
 ****** d = 99999999999999999999999999999999.9;
 long l = (long)d;
 System.out.println(l);
}

Вывод:

9223372036854775807

Это меня беспокоит. Я не могу продолжать работать с совершенно другим числом. Я предпочел бы получить ошибку или исключение.

Есть ли способ обнаружить это на Java?

2 ответа

Вы можете сравнить его с Long.MIN_VALUE и Long.MAX_VALUE:

public static boolean fitsLong(****** d) {
 return d >= Long.MIN_VALUE && d < Long.MAX_VALUE;
}

Несколько более сложный подход заключается в использовании BigDecimal:

****** value = 1234567.9;
long l = BigDecimal.valueOf(value)
 .setScale(0, RoundingMode.HALF_EVEN)
 .longValueExact(); // 1234568
****** value = 99999999999999999999999999999999.9;
long l = BigDecimal.valueOf(value)
 .setScale(0, RoundingMode.HALF_EVEN)
 .longValueExact(); // ArithmeticException

Таким образом вы можете контролировать, как выполняется округление.

Вы можете спросить, почему существует строгое неравенство в fitsLong: d < Long.MAX_VALUE. На самом деле это потому, что сам Long.MAX_VALUE не может быть представлен как двойной номер. Когда вы создаете (******)Long.MAX_VALUE, в ****** для представления его недостаточно точности, поэтому выбирается самое близкое представляемое значение, которое равно 9223372036854775808.0 (Long_MAX_VALUE+1.0). Таким образом, он d <= Long.MAX_VALUE возвратил бы true для числа, которое на самом деле немного больше, так как при этом сравнении константа Long.MAX_VALUE повышается до двойного типа. С другой стороны, Long.MIN_VALUE может быть точно представлен в типе ******, поэтому здесь мы имеем >=.

Также интересно, почему следующие работы:

****** value = -9223372036854775809.9; // Long.MIN_VALUE-1.9
System.out.println(fitsLong(value)); // returns true

Это потому, что вы на самом деле ничего не вычитали из Long.MIN_VALUE. См:

****** d1 = Long.MIN_VALUE;
****** d2 = -9223372036854775809.9;
System.out.println(d1 == d2); // true

Двойной точности недостаточно, чтобы различать -9223372036854775808 и -9223372036854775809.9, поэтому на самом деле это же двойное число. Во время компиляции он преобразуется в двоичную форму, а двоичная форма для этих двух чисел одинакова. Таким образом, скомпилировав программу, вы не можете отличить, был ли -9223372036854775808 или -9223372036854775809.9 в исходном коде.

Если вы чувствуете, что все еще проблема, постройте BigDecimal из String:

long l = new BigDecimal("-9223372036854775808.2")
 .setScale(0, RoundingMode.HALF_EVEN)
 .longValueExact(); // ok, -9223372036854775808
long l = new BigDecimal("-9223372036854775808.9")
 .setScale(0, RoundingMode.HALF_EVEN)
 .longValueExact(); // ArithmeticException


Когда вы применяете тип с плавающей запятой к int или long, результатом будет либо ближайшее целое число (округление к нулю), либо MIN_VALUE или MAX_VALUE для int или long, См. JLS 5.1.3.

Следовательно, одним из альтернатив было бы сделать typecast, а затем проверить подходящие MIN_VALUE или MAX_VALUE.

Обратите внимание, что Long.MAX_VALUE - 9223372036854775807... который является номером вашей тестовой программы!

(Однако это не работает, если вы производите тип с плавающей точкой в ​​ byte, char или short. См. ссылку выше для объяснения.)

licensed under cc by-sa 3.0 with attribution.