为什么在java操作中有些双精度除法是正确的



我了解二进制数的理论,所以双数运算是不精确的。然而,在java中,我不知道为什么"(双(65/100〃;是0.65,这在小数中是完全正确的,而不是0.6550000000004

double a = 5;
double b = 4.35;
int c = 65;
int d = 100;
System.out.println(a -  b); // 0.6500000000000004
System.out.println((double) c /  d); // 0.65

Java完全搞砸了有自己的处理浮点二进制到十进制转换的方法。

一个简单的C程序(用gcc编译(给出了结果:

printf("1: %.20fn", 5.0 - 4.35);         // 0.65000000000000035527
printf("2: %.20fn", 65./100);            // 0.65000000000000002220

Java给出了结果(注意,您只需要17位数字就可以看到它,但我正在努力让它更清楚(:

System.out.printf("%.20fn", 5.0 - 4.35); // 0.65000000000000040000
System.out.printf("%.20fn", 65./100);    // 0.65000000000000000000

但当使用%a格式说明符时,两种语言都会使用printf底层的十六进制(正确(值:0x1.4ccccccccccd00000000p-1

因此,Java在代码中的某个点执行了一些非法舍入这里明显的问题是,Java有一套不同于Java规范的将二进制转换为十进制的规则:

m或a的小数部分的结果中的位数等于精度。如果未指定精度,则默认值为6。如果精度小于Float.toString(Float(或Double.toString(Double(分别返回的字符串中小数点后的位数,则将使用四舍五入半上算法对该值进行取整否则,可能会附加零以达到精度对于值的规范表示,请根据需要使用Float.toString(Float(或Double.toString(Double((强调矿(

toString规范中:

m或a的小数部分必须打印多少位数字?必须至少有一个数字来表示小数部分,并且超过这个数字,但只能是唯一区分参数值和double类型的相邻值所需的数字。。也就是说,假设x是由这种方法对有限的非零自变量d产生的十进制表示所表示的精确数学值。那么d必须是最接近x的二重值;或者,如果两个双精度值与x相等,则d必须是其中之一,并且d的有效位的最低有效位必须为0(强调矿(

因此,Java确实执行了与C不同的二进制到十进制转换,但它仍然比其他任何值都更接近真实的二进制值,因此规范保证二进制值可以通过十进制到二进制转换恢复。

William Kahan教授在本文中警告了一些Java浮点问题:

Java的浮点如何伤害每一个

但这种转换行为似乎是IEEE的抱怨。

编辑:我在评论中包含了@MarkDickinson提供的信息,以报告这种Java行为,尽管与C不同,但有文档记录,并且符合IEEE。这已经在这里、这里和这里解释过了。

最新更新