浮点数精确到多少位?

2021-11-07 11:23:44 +08:00
 Higurashi

Java 采用 IEEE 754 实现浮点数。

所以我们会看到(见链接部分),在二进制中,float 类型有 24 位精确,在十进制中,float 类型有 log10(2^24)≈7 位精确。

我想问的是,使用 log10(2^24) 这个公式来由二进制精度计算十进制精度的的依据是什么?

我找了一些常见的解释,但还是不能很好地理解。

谢谢。


[算法] 解析 IEEE 754 标准

Java 中 float/double 取值范围与精度

Why IEEE754 single-precision float has only 7 digit precision?

1507 次点击
所在节点    问与答
15 条回复
GTim
2021-11-07 11:51:37 +08:00
因为 2^24 = 16777216 ; 9.9999 ^ n < 16777216 < 9.999 ^ (n+1) 可以知道 n = 7
shintendo
2021-11-07 11:57:09 +08:00
首先,是有效数字 7 位,不是小数点后 7 位,由于小数点位置保存在另一个部分,只看有效数字部分可以当作整数来看,这个整数是用 24 位二进制表示的,所以它的范围是 1 到 2^24 ,对应的十进制数值就是 1 到 log10(2^24)了
Higurashi
2021-11-07 11:59:59 +08:00
@GTim #1 感谢回复。是的,的确是这样,但我不明白的是,为什么计算得到的 n 就是十进制下的精确位数?[10506083.html#a4]( https://www.cnblogs.com/HDK2016/p/10506083.html#a4) 中也作了类似说明:

```
在单精度浮点数中的二进制小数位有 23 个,所能表示 2^23 个数,那么只需要换算成在 10 进制下能够表示相同个数的位数,就可以得到精度了。
10^n = 223
10^n = 8388608
10^6 < 8388608 < 107
所以但精度浮点数的精度为 6 位,同理也可以得到双精度浮点数的精度为 15 位。
```

我疑惑的其实是这背后的理由是什么。
oldshensheep
2021-11-07 12:15:04 +08:00
log10(2^24) = x ≈ 7.225
就是 10^x=16777216
2^24=10^x 换成这样应该明白了吧
Higurashi
2021-11-07 13:17:30 +08:00
@shintendo #2 @oldshensheep #4 感谢回复。我稍微懂得了一些。24 位二进制最大为 (1111 1111 1111 1111 1111 1111)2 = (2^24-1)10 ,观察到 100(10^2)<=y<1000(10^3),此时 y 有 [log10y] + 1 = 3 个有效位,类似可以知道 2^24-1 有 [log10(2^24-1)] + 1 个有效位。
但是小于 2^24-1 的 24 位二进制数转换为 10 进制也还是 [log10(2^24-1)] + 1 个有效位吗?比如 (0000 0000 0000 0000 0000 001)2 在十进制中有多少个有效位?
shintendo
2021-11-07 17:59:53 +08:00
@Higurashi
但是小于 2^24-1 的 24 位二进制数转换为 10 进制也还是 [log10(2^24-1)] + 1 个有效位吗?
不一定,可能更少,但不会更多。这个很简单吧,一个自然数数值减小,它的位数只可能不变或减少。

其实“位数”只是一个粗略的描述,你从有效数字的范围而不是位数出发更容易理解精度:
1. 24 位二进制能表示的整数范围是[0 - 2^24]
2. 所以如果一个浮点数的有效数字小于 2^24 ,就能够被精确表示,反之则无法被精确表示
3. 由于 2^24=16777216 是一个 8 位数,所有的 7 位数都小于它,因此有效数字在 7 位以内的数一定能够被精确表示,因此说该格式的精度是 7 位
4. 对于有效数字 8 位的数,一部分(小于 16777216 的)可以精确表示,一部分(大于 16777216 的)不能精确表示;有效数字 9 位以上的数,全部不能精确表示。
Higurashi
2021-11-09 11:21:09 +08:00
@shintendo #6
的确,不同的场景有不同的所谓“精度”。我在 https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ 中看到了一种定义:
“For most of our purposes when we say that a format has n-digit precision we mean that over some range, typically [10^k, 10^(k+1)), where k is an integer, all n-digit numbers can be uniquely identified.”
我觉得暂时只讨论清楚这样一种“精度”比较好,所以现在我的疑惑是,[10^k, 10^(k+1)) 范围内的数字,它能够精确到多少位?是如何得到的?比如,我在源代码中写下一个浮点数,我如何知道从第几位开始程序就不再能够准确地将其表示?
链接指向的文章对此进行了解释,但说实话,我还看不太懂。
Higurashi
2021-11-10 08:22:47 +08:00
@shintendo #6
抱歉我没有仔细理解。我想我懂得了你的意思,现在总结一下。
通常所说的精确到多少位(比如 7 ),指的是当我们在源代码中添加一个浮点数字面量时,浮点数超过第 7 位的有效位可能不能够准确表示。
shintendo
2021-11-10 10:15:55 +08:00
@Higurashi
不只是写在源代码里的,也包括计算产生的数,当我们用 IEEE 754 这种格式去表达一个实数时,前 7 位是可靠的,第 8 位不一定,第 9 位一定不可靠
shintendo
2021-11-10 10:21:55 +08:00
之所以有一个“不一定”的位,是因为二进制的全 1 对应到 10 进制并不是全 9 。如果你用二进制来看,实际数值相比存储数值,前 24 位可靠(也只有 24 位),24 位以后就丢弃了。
Higurashi
2021-11-10 12:29:40 +08:00
@shintendo #9
意思是 23 bit 所保存着的小数转化(数学上的转化,不进行舍入)为十进制数后最多包含 8 位有效数字,且并不是所有拥有 8 位有效位的十进制小数都能够被准确保存?
shintendo
2021-11-10 13:29:22 +08:00
@Higurashi
8 位有效数字的,10000000 - 16777215 可以准确保存,16777216 - 99999999 不能准确保存,见 6 楼
shintendo
2021-11-10 14:19:33 +08:00
@Higurashi
我发现我好像也误解了你的问题,你问的是二进制转十进制这个过程产生的精度问题?
Higurashi
2021-11-10 19:43:39 +08:00
@shintendo #12
嗯,“不只是写在源代码里的,也包括计算产生的数”是不是指这样一种情况:
int a = 16777215;
int b = a * 10 + 1;
System.out.println((float)a);
System.out.println((float)b);
-----
1.6777215E7
1.67772144E8
在这里 167772151 为“计算产生的数”,其超过了单精度的精确范围,所以转换为单精度时不能够被准确表示。
Higurashi
2021-11-10 21:22:38 +08:00
@shintendo #13
暂时只是十进制转二进制过程中的精度问题。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/813602

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX