Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/55.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么C和Java循环浮动不同?_Java_C_Floating Point_Printf - Fatal编程技术网

为什么C和Java循环浮动不同?

为什么C和Java循环浮动不同?,java,c,floating-point,printf,Java,C,Floating Point,Printf,考虑浮点数0.644696875。让我们使用Java和C将其转换为八位小数的字符串: JAVA import java.lang.Math; 公共类圆形示例{ 公共静态void main(字符串[]args){ System.out.println(String.format(“%10.8f”,0.644696875)); } } 结果:0.64469688 你自己试试吧: C #包括 int main() { printf(“%10.8f”,0.644696875);//双精度转换为字符串

考虑浮点数0.644696875。让我们使用Java和C将其转换为八位小数的字符串:

JAVA
import java.lang.Math;
公共类圆形示例{
公共静态void main(字符串[]args){
System.out.println(String.format(“%10.8f”,0.644696875));
}
}
结果:0.64469688

你自己试试吧:

C
#包括
int main()
{
printf(“%10.8f”,0.644696875);//双精度转换为字符串
返回0;
}
结果:0.64469687

你自己试试吧:

问题 为什么最后一个数字不同

背景 数字0.644696875不能准确表示为机器编号。它表示为分数2903456606016923/4503599627370496,其值为0.64469687499999

这无疑是一个边缘案例。但我真的很好奇这种差异的来源


相关:

这里可能发生的情况是,他们使用稍微不同的方法将数字转换为字符串,这会引入舍入错误。在编译期间将字符串转换为浮点值的方法在它们之间也可能不同,这同样会由于舍入而给出稍有不同的值

但是请记住,float的分数有24位精度,精确到~7.22个十进制数字[log10(2)*24],前7位在它们之间是一致的,所以最后几个最低有效位是不同的

欢迎来到有趣的浮点数学世界,2+2并不总是等于4。

在这种情况下,Java规范需要麻烦的双舍入。数字0.64469687499999947064566185872536126012420654296875首先转换为0.644696875,然后四舍五入为0.64469688

相比之下,C实现只是将0.6446968749999947064566185872536126012420654296875直接舍入到八位数字,生成0.64469687

预备赛 对于
Double
,Java使用IEEE-754基本64位二进制浮点。在此格式中,与源文本中的数字0.644696875最接近的值是0.6446968749999947064566185872536126012420654296875,我相信这是使用
String.format(“%10.8f”,0.644696875)
格式化的实际值

Java规范所说的 说:

…如果精度小于分别由
Float.toString(Float)
Double.toString(Double)
返回的字符串中小数点后出现的位数,则将使用向上舍入算法对值进行舍入。否则,可以添加零以达到精度

让我们考虑“…返回的字符串<代码> double totostring(double)< /代码>。对于数字0.64469687499999947064566185872536126012420654296875,此字符串为“0.644696875”。这是因为Java规范说明了这一点,“0.644696875”在本例中只有足够的数字。2

这个数字在小数点后有九位,而
“%10.8f”
要求八位,所以上面引用的段落说“值”是四舍五入的。它是指
格式的实际操作数,即0.6446968749999947064566185872536126012420654296875,还是它提到的字符串“0.644696875”?由于后者不是数值,我希望“值”是指前者。但是,第二句说“否则[即,如果请求更多的数字],可能会追加零…”如果我们使用的是
格式的实际操作数,我们将显示其数字,而不是使用零。但是,如果我们将字符串作为一个数值,那么它的十进制表示形式在显示的数字后面只有零。因此,这似乎是我们想要的解释,Java实现似乎也符合这一点

因此,要使用
“%10.8f”
格式化此数字,我们首先将其转换为0.644696875,然后使用四舍五入规则对其进行四舍五入,从而生成0.64469688

这是一个糟糕的规范,因为:

  • 它需要两个舍入,这会增加误差
  • 环形交叉发生在难以预测和控制的地方。某些值将在小数点后两位舍入。有些将在13后四舍五入。一个程序不能很容易地预测或调整它
(还有,他们写了“可以”附加的零,这是一件很遗憾的事。为什么不“否则,零附加以达到精度”?“可以”似乎给了实现一个选择,尽管我怀疑他们的意思是“可以”是基于是否需要零来达到精度,而不是基于实现者是否选择附加它们。)

脚注 1当源文本中的
0.644696875
转换为
Double
时,我相信结果应该是
Double
格式中可表示的最接近的值。(我没有在Java文档中找到这一点,但它符合要求实现行为相同的Java哲学,我怀疑转换是根据
Double.valueOf(String s)
完成的。)与0.644696875最接近的
Double
是0.6446968749999999470645661858725326012420654296875


2如果数字较少,则七位数字0.64469687不足,因为最接近它的
双精度值为0.6446968699999997745192104048328474164009423828125。因此,需要8位数字来唯一区分0.64469687499999947064566185872536126012420654296875,请注意,问题可能与二进制表示无关,但与每种语言中的
printf
函数的工作方式有关。默认舍入行为是一种有点随意的选择。Java,GNULIBC。重点是:Jav
#include <stdio.h>

int main()
{
    printf("%10.8f", 0.644696875); //double to string
    return 0;
}