为什么在java中String.format 0.1d double值精确为0.1?

Che*_* Li 6 java ieee-754

IEEE 754浮点数是离散的。

public class MyTest2 {
  public static void main(String[] args) {
    //about 1.00000001490116119384765625E-1 in IEEE-754
    float f = 0.1f;
    //about 1.00000000000000005551115123126E-1 in IEEE-754
    double d = 0.1d;
    System.out.println(String.format("double 0.1= %.30f", d));
    System.out.println(String.format("float 0.1 = %.15f", f));
    System.out.println(d+"");
  }
}
Run Code Online (Sandbox Code Playgroud)

请参阅在 IdeOne.com 上实时运行的代码。在JDK8中运行,输出为

double 0.1= 0.100000000000000000000000000000
float 0.1 = 0.100000001490116
0.1
Run Code Online (Sandbox Code Playgroud)

浮点值按预期打印。我希望打印双精度值 0.1d 类似 1.000000000000000055511151231260。为什么它在小数部分打印全零

如果我将双精度变量 d 转换为字符串,它会打印 0.1。

System.out.println(d+"");
Run Code Online (Sandbox Code Playgroud)

java如何将最接近的浮点值0.1d(大约为1.00000001490116119384765625E-1)转换为精确的0.1?

Eri*_*hil 6

Java 规范要求这种不完美的值显示。该f格式仅产生与该方法产生的有效数字一样多的有效数字Double.toString(double),然后无意识地附加零以达到所要求的精度。

\n

根据文档,对于f格式,如果精度超过将产生的小数点后的位数Double.toString(double),则可以附加 \xe2\x80\x9czeros 以达到精度。\xe2\x80\x9d 这没有说明什么这些零被附加到。据推测,它们被附加到将生成的字符串中Double.toString(double)

\n

的文档Double.toString(double)说它会产生许多 \xe2\x80\x9cas ,但只是需要尽可能多的数字来唯一区分参数值与类型的相邻值。\xe2\x80\x9d 我在这里double进一步讨论。对于 0.1000000000000000055511151231257827021181583404541015625,生成 \xe2\x80\x9c0.1\xe2\x80\x9d。(相邻值 0.099999999999999999167332731531132594682276248931884765625 和\n0.1000000000000000194289029309402394574135541915893554687 5,都比 0.1000000000000000055511151231257827021181583404541015625 更远离 .1,并且它们的格式为 \xe2\x80\x9c0.09999999999999999\xe2\x80\x9d 和 \xe2\ x80\x9c0.10000000000000002 \xe2\x80\x9d,因此 \xe2\x80\x9c0.1\xe2\x80\x9d 用于唯一区分 0.1000000000000000055511151231257827021181583404541015625 与其邻居。)Double.toString(double)

\n

因此,System.out.println(String.format("double 0.1= %.30f", d))从 \xe2\x80\x9c0.1\xe2\x80\x9d 开始Double.toString(double),并附加 29 个零。

\n

同样,如果更改d为 0.099999999999999999167332731531132594682276248931884765625,String.format则会生成 \xe2\x80\x9c0.099999999999999990000000000000\xe2\x80\x9d\ xe2\x80\x94it 已获取结果toString并附加零。对于 0.10000000000000001942890293094023945741355419158935546875 ,它生成 \xe2\x80\x9c0.100000000000000020000000000000\xe2\x80\x9d 。

\n

这符合规范。指定的行为无法正确呈现真实值,因此我认为该规范有缺陷。

\n

顺便说一句,无论请求的精度大于还是小于将产生的位数,Java 规范都会带来麻烦Double.toString(double)。在请求权限较小的情况下,Java规范要求进行双舍入,这会增加错误。

\n