【发布时间】:2020-01-06 18:30:57
【问题描述】:
考虑浮点数 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.64469688
自己试试吧:http://tpcg.io/oszC0w
C
#include <stdio.h>
int main()
{
printf("%10.8f", 0.644696875); //double to string
return 0;
}
结果:0.64469687
自己试试吧:http://tpcg.io/fQqSRF
问题
为什么最后一位数字不同?
背景
数字 0.644696875 不能完全表示为机器编号。它表示为分数 2903456606016923 / 4503599627370496,其值为 0.6446968749999999
这无疑是一个边缘案例。但我真的很好奇差异的来源。
相关:https://mathematica.stackexchange.com/questions/204359/is-numberform-double-rounding-numbers
【问题讨论】:
-
请注意,这个问题可能与二进制表示无关,而是
printf函数在每种语言中的工作方式。 -
默认的舍入行为是一个有点随意的选择。 Java seems to use banker's rounding,GNU libc seems to round to the nearest integer by default。
-
要点是:Java 将使用具有最少位数的值,这将唯一标识该值。这是
0.644696875,将四舍五入为0.64469688。相反,C 将使用精确的 double 值,大约是0.64469687499999999997363220316515253....;这是四舍五入向下到0.64469687 -
@Ctx - 什么,不,你说的都不对。 Java 和 C 都使用 uhh 52 位整数乘以 2 的 11 位整数指数的幂。仅当将此表示形式转换为供人类使用的字符串时,才会出现十进制数字,除非您使用的是任意精度的类,否则内部不涉及十进制数字。
-
@Ctx:是的,使用四舍五入的 C 实现(C 标准比 Java 标准更宽松,不需要这样做)比 Java 实现更准确,因为根据我的在下面回答,Java 在格式化时需要 两次 舍入操作,这会增加错误。
标签: java c floating-point printf