考虑浮点数0.644696875。让我们使用Java和C将其转换为带有八个小数的字符串:
java
import java.lang.Math;
public class RoundExample{
public static void main(String[] args){
System.out.println(String.format("%10.8f",0.644696875));
}
}
结果:0.6446968 8
自己尝试:http://tpcg.io/oszC0w
C
#include <stdio.h>
int main()
{
printf("%10.8f", 0.644696875); //double to string
return 0;
}
结果:0.6446968 7
自己尝试:http://tpcg.io/fQqSRF
问题
为什么最后一位数字不同?
背景
数字0.644696875无法完全表示为机器编号。它表示为分数2903456606016923/4503599627370496,其值为0.6446968749999999
诚然,这是一个极端情况。但是我真的很好奇差异的根源。
相关:https://mathematica.stackexchange.com/questions/204359/is-numberform-double-rounding-numbers
最佳答案
结论
在这种情况下,Java规范需要麻烦的双舍入。数字0.6446968749999999470645661858725361526012420654296875首先转换为0.644696875,然后四舍五入为0.64469688。
相反,C实现仅将0.6446968749999999470645661858725361526012420654296875直接舍入为八位数字,从而产生0.64469687。
初赛
对于Double
,Java使用IEEE-754基本的64位二进制浮点数。在此格式下,最接近源文本中数字0.644696875的值为0.6446968749999999470645661858725361526012420654296875,我相信这是将使用String.format("%10.8f",0.644696875)
.1格式化的实际值。
Java规范怎么说
The documentation for formatting with the Double
type and f
format说:
让我们考虑“…Double.toString(double)
返回的字符串”。对于数字0.6446968749999999470645661858725361526012420654296875,此字符串为“0.644696875”。这是因为Java规范说 toString
produces just enough decimal digits to uniquely distinguish the number within the set of Double
values和“0.644696875”在这种情况下仅具有足够的数字。2
该数字在小数点后有9位数字,而"%10.8f"
请求有8位数字,因此上面引用的段落表示“值”是四舍五入的。它是什么意思— format
的实际操作数为0.6446968749999999470645661858725361526012420654296875,或它提到的字符串“0.644696875”?由于后者不是数字值,因此我希望“值”表示前者。但是,第二句话说:“否则[即,如果需要更多位数],可以附加零...”。如果我们使用的是format
的实际操作数,我们将显示其位数,而不使用零。但是,如果我们将字符串作为数字值,则其十进制表示形式将仅在显示的数字之后为零。因此,这似乎是预期的解释,并且Java实现似乎与此相符。
因此,要使用"%10.8f"
格式化此数字,我们首先将其转换为0.644696875,然后使用舍入上舍入规则将其舍入,这将产生0.64469688。
这是一个糟糕的规范,因为:
(此外,他们在“可能”之后添加了零,这是很可惜的。为什么不另外添加“零来达到精度”?为什么带有“may”,他们似乎给了实现一个选择,尽管我怀疑它们的意思是“可能”是基于是否需要零才能达到精度,而不是取决于实现者是否选择附加它们。)
脚注
1当源文本中的
0.644696875
转换为Double
时,我相信结果应该是Double
格式可表示的最接近的值。 (我没有在Java文档中找到它,但是它符合要求实现必须具有相同行为的Java哲学,并且我怀疑转换是按照Double.valueOf(String s)
进行的,即does require this。)最接近0.644696875的Double
是0.6446968749999999470645661858725361526012420654296875。2用较少的数字表示,七位数的0.64469687是不够的,因为最接近它的
Double
值为0.6446968 699999999774519210404832847416400909423828125 。因此,需要八位数字来唯一区分0.6446968 749999999470645661858725361526012420654296875 。