Разница между двойным значением в Oracle и Java (IEEE 754)
BINARY_DOUBLE в Oracle и Double в Java используют стандарт IEEE 754.
Но есть разница в их точности.
Например значение:
456.67d
Oracle:
declare
a BINARY_DOUBLE := 456.67d;
begin
SYS.DBMS_OUTPUT.PUT_Line(TO_CHAR(a,'9.99999999999999999999EEEE'));
end;
Результат: 4.56670000000000020000E+02
Джава:
Double d = 456.67d;
DecimalFormat formatter = new DecimalFormat("0.000000000000000000000E000");
System.out.println(formatter.format(d));
Результат: 4.566700000000000000000E002
Значение в Java не так точно, как в Oracle.
Онлайн-конвертеры сообщили, что наиболее точным представлением для 456.67d является:
4.56670000000000015916157281026E2
Так почему же в Java точность не такая, как в Oracle? И как я могу получить в Java более точное значение?
2 ответа
Использовать BigDecimal:
Double d = 456.67d;
BigDecimal bd = new BigDecimal( d );
bd.setScale( 50 );
System.out.println( bd );
DecimalFormat formatter = new DecimalFormat("0.000000000000000000000E000");
System.out.println( formatter.format( bd ) );
Выход:
456.67000000000001591615728102624416351318359375
4.566700000000000159162E002
Оба точны. Они просто используют другую стратегию для печати.
Вот точные значения, напечатанные с помощью Squeak Smalltalk (но язык не имеет значения, это то же представление и та же арифметика):
d := 456.67.
{
d predecessor asFraction printShowingMaxDecimalPlaces: 50.
d asFraction printShowingMaxDecimalPlaces: 50.
d successor asFraction printShowingMaxDecimalPlaces: 50}.
->
#(
'456.6699999999999590727384202182292938232421875'
'456.67000000000001591615728102624416351318359375'
'456.670000000000072759576141834259033203125'
)
Так что любое десятичное представление между (d predecessor asFraction + d asFraction) / 2 printShowingMaxDecimalPlaces: 50.
а также (d successor asFraction + d asFraction) / 2 printShowingMaxDecimalPlaces: 50.
будет округлено до того же значения с плавающей запятой (исключены границы, поскольку точная связь округляется до ближайшего четного, а это число имеет нечетное значениеи...)
Итак, границы:
#(
'456.669999999999987494447850622236728668212890625'
'456.670000000000044337866711430251598358154296875'
)
Oracle просто использует 17 цифр и не беспокоится о дальнейших цифрах, потому что известно, что 17 достаточно для различения любых двух значений с плавающей запятой двойной точности.
Java немного более умна: она использует только достаточно цифр, чтобы отличать их от следующих представимых значений с плавающей запятой. Таким образом, размещение 0, 1, 2, 3 или 4 в позиции 17 или даже 4.5666999999999999e2 не имеет значения, это то же самое значение с плавающей запятой.