Oracle 中的双精度值与 Java (IEEE 754) 之间的区别
Difference between double value in Oracle and Java (IEEE 754)
Oracle 中的 BINARY_DOUBLE 和 Java 中的 Double 使用 IEEE 754 标准。
但是他们的准确度是有区别的
例如值:
456.67d
甲骨文:
declare
a BINARY_DOUBLE := 456.67d;
begin
SYS.DBMS_OUTPUT.PUT_Line(TO_CHAR(a,'9.99999999999999999999EEEE'));
end;
Result: 4.56670000000000020000E+02
Java:
Double d = 456.67d;
DecimalFormat formatter = new DecimalFormat("0.000000000000000000000E000");
System.out.println(formatter.format(d));
Result: 4.566700000000000000000E002
Java 中的值不如 Oracle 中的值准确。
在线转换器表示 456.67d 的最准确表示是:
4.56670000000000015916157281026E2
那么为什么 Java 中的准确性与 Oracle 中的不一样?
以及如何获得 Java 更准确的值?
使用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都没有关系,还是一样的浮点数。
BINARY_DOUBLE 和 Java 中的 Double 使用 IEEE 754 标准。
但是他们的准确度是有区别的
例如值:
456.67d
甲骨文:
declare
a BINARY_DOUBLE := 456.67d;
begin
SYS.DBMS_OUTPUT.PUT_Line(TO_CHAR(a,'9.99999999999999999999EEEE'));
end;
Result: 4.56670000000000020000E+02
Java:
Double d = 456.67d;
DecimalFormat formatter = new DecimalFormat("0.000000000000000000000E000");
System.out.println(formatter.format(d));
Result: 4.566700000000000000000E002
Java 中的值不如 Oracle 中的值准确。
在线转换器表示 456.67d 的最准确表示是:
4.56670000000000015916157281026E2
那么为什么 Java 中的准确性与 Oracle 中的不一样? 以及如何获得 Java 更准确的值?
使用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都没有关系,还是一样的浮点数。