You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Java中float赋值给double时输出异常小数的原因探究

为什么float转double后输出会有额外小数?

嘿,这个问题本质是浮点数在计算机中的二进制存储机制导致的,我给你一步步拆解清楚~

首先先纠正你代码里的小笔误:应该是double b = a;对吧?不然g是未定义变量,编译都跑不起来😉

核心原因:浮点数无法精确表示所有十进制小数

9.8这种十进制小数,在二进制里是无限循环的小数,就跟十进制里的1/3=0.3333...一样,计算机只能用有限的位数去存储它的近似值——而float和double的存储位数不同,近似的精度也就不一样。

1. 先看float a = 9.8f;

float是单精度浮点数,总共32位,其中只有23位用来存储尾数(也就是小数部分的有效位)。当你把9.8赋值给float时,它只能存储最接近9.8的二进制近似值——这个值本身就不是精确的9.8,只是float能达到的最接近的结果。

2. 再看double b = a;

当把float转成double时,double是双精度浮点数(64位,52位尾数),它会原封不动地把float里存储的那个近似值“搬”过来,扩展成double的格式。注意:这一步不会去重新计算更接近9.8的double值,只是把float的近似值完整保留。所以float存储时的误差,在转成double后就被更清晰地展现出来了,也就出现了9.800000190734863这个看起来奇怪的数。

3. 最后看double c = 9.8;

而直接给double赋值9.8时,double会用自己更高的精度(52位尾数)去存储最接近9.8的二进制近似值。这个近似值比float的版本要接近9.8得多,当Java的System.out.println打印它时,会做自动格式化:只要这个值和9.8的差距小到一定程度,就会直接显示成9.8(其实底层还是有微小误差,但肉眼看不到了)。

举个生活化的类比

就像你用一个只能记3位小数的本子写下1/3=0.333,然后把这个数抄到一个能记10位小数的本子上,就变成0.3330000000;但如果你直接在10位小数的本子上写1/3,会是0.3333333333——前者是抄录低精度的近似值,后者是用高精度直接存储的近似值,看起来自然不一样。

验证小代码

你可以跑这段代码感受一下:

float a = 9.8f;
double b = a;
double c = 9.8;
System.out.println(a); // 打印9.8,因为float的toString会格式化掩盖误差
System.out.println(b); // 完整展现float转double后的近似值
System.out.println(c); // double的近似值足够接近,格式化后显示9.8

总结

  • 变量b的额外小数,来自float存储9.8时的二进制近似误差,转成double后这个误差没有被格式化掩盖,完整呈现了出来。
  • 变量c是直接用double的高精度存储9.8的近似值,误差极小,被println格式化后显示为我们熟悉的9.8

内容的提问来源于stack exchange,提问作者Dawson Smith

火山引擎 最新活动