结论
在这种情况下,Java规范需要一个麻烦的双舍入。数字 0.6446968749999999470645661858725361526012420654296875 首先转换为 0.644696875,然后舍入为 0.64469688。
相反,C 实现只是将 0.6446968749999999470645661858725361526012420654296875 直接舍入为八位数字,生成 0.64469687。
预赛
对于 ,Java 使用 IEEE-754 基本的 64 位二进制浮点。在这种格式中,最接近源文本中数字的值0.644696875是0.644696874999999470645661858725361526012420654296875,我相信这是要格式化的实际值。1 个Double
String.format("%10.8f",0.644696875)
Java规范是怎么说的
使用 Double
类型和 f
格式进行格式化的文档说:
...如果精度小于由 或 分别返回的字符串中小数点后出现的位数,则将使用舍入半上舍入算法舍入该值。否则,可以附加零以达到精度...Float.toString(float)
Double.toString(double)
让我们考虑“返回的字符串...".对于数字 0.6446968749999999470645661858725361526012420654296875,此字符串为“0.644696875”。这是因为 Java 规范规定 toString
生成的十进制数字刚好足以唯一区分 Double
值集中的数字,而“0.644696875”在这种情况下只有足够的数字。阿拉伯数字Double.toString(double)
该数字在小数点后有九位数字,并请求八位,因此上面引用的段落说“值”四舍五入。它表示哪个值 — 的实际操作数,即 0.6446968749999999470645661858725361526012420654296875,或者它提到的字符串“0.644696875”?由于后者不是数值,我本来以为“值”是指前者。但是,第二句话说:“否则[即,如果请求更多数字],则可以附加零......”如果我们使用 的实际操作数,我们将显示其数字,而不是使用零。但是,如果我们将字符串作为数值,则其十进制表示形式在其中显示的数字之后将只有零。因此,这似乎是预期的解释,Java实现似乎符合这一点。"%10.8f"
format
format
因此,要将此数字的格式设置为 ,我们首先将其转换为 0.644696875,然后使用舍入半向上规则将其舍入,该规则产生 0.64469688。"%10.8f"
这是一个糟糕的规范,因为:
- 它需要两个舍入,这可能会增加误差。
- 舍入发生在难以预测和难以控制的地方。某些值将在小数点后两位后四舍五入。有些将在13之后四舍五入。程序无法轻松预测或调整它。
(另外,很遗憾他们写了零“可能”附加。为什么不“否则,追加零以达到精度”?对于“may”,他们似乎给了实现一个选择,尽管我怀疑他们的意思是“可能”是基于是否需要零来达到精度,而不是实现者是否选择追加它们。
脚注
1.当在源文本转换为时,我认为结果应该是最接近的格式中可表示的值。(我没有在Java文档中找到它,但它符合Java的理念,即要求实现具有相同的行为,我怀疑转换是根据 完成的,这确实需要这样做。最接近 0.644696875 的是 0.6446968749999999470645661858725361526012420654296875。0.644696875
Double
Double
Double.valueOf(String s)
Double
2 如果数字较少,七位数 0.64469687 是不够的,因为最接近它的值为 0.6446968699999999774519210404832847416400909423828125。因此,需要八位数字才能唯一区分0.6446968749999999470645661858725361526012420654296875。Double