Oracle和Java的双重值之间的差异(IEEE 754)



binary_double in oracle中的binary_double,double in 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

java:

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更准确的价值?

使用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.566699999999999999E2都不重要,它仍然是相同的浮点值。

最新更新