V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Higurashi
V2EX  ›  问与答

浮点数精确到多少位?

  •  
  •   Higurashi · 2021-11-07 11:23:44 +08:00 · 1381 次点击
    这是一个创建于 872 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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?

    15 条回复    2021-11-10 21:22:38 +08:00
    GTim
        1
    GTim  
       2021-11-07 11:51:37 +08:00
    因为 2^24 = 16777216 ; 9.9999 ^ n < 16777216 < 9.999 ^ (n+1) 可以知道 n = 7
    shintendo
        2
    shintendo  
       2021-11-07 11:57:09 +08:00
    首先,是有效数字 7 位,不是小数点后 7 位,由于小数点位置保存在另一个部分,只看有效数字部分可以当作整数来看,这个整数是用 24 位二进制表示的,所以它的范围是 1 到 2^24 ,对应的十进制数值就是 1 到 log10(2^24)了
    Higurashi
        3
    Higurashi  
    OP
       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
        4
    oldshensheep  
       2021-11-07 12:15:04 +08:00
    log10(2^24) = x ≈ 7.225
    就是 10^x=16777216
    2^24=10^x 换成这样应该明白了吧
    Higurashi
        5
    Higurashi  
    OP
       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
        6
    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
        7
    Higurashi  
    OP
       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
        8
    Higurashi  
    OP
       2021-11-10 08:22:47 +08:00
    @shintendo #6
    抱歉我没有仔细理解。我想我懂得了你的意思,现在总结一下。
    通常所说的精确到多少位(比如 7 ),指的是当我们在源代码中添加一个浮点数字面量时,浮点数超过第 7 位的有效位可能不能够准确表示。
    shintendo
        9
    shintendo  
       2021-11-10 10:15:55 +08:00
    @Higurashi
    不只是写在源代码里的,也包括计算产生的数,当我们用 IEEE 754 这种格式去表达一个实数时,前 7 位是可靠的,第 8 位不一定,第 9 位一定不可靠
    shintendo
        10
    shintendo  
       2021-11-10 10:21:55 +08:00
    之所以有一个“不一定”的位,是因为二进制的全 1 对应到 10 进制并不是全 9 。如果你用二进制来看,实际数值相比存储数值,前 24 位可靠(也只有 24 位),24 位以后就丢弃了。
    Higurashi
        11
    Higurashi  
    OP
       2021-11-10 12:29:40 +08:00
    @shintendo #9
    意思是 23 bit 所保存着的小数转化(数学上的转化,不进行舍入)为十进制数后最多包含 8 位有效数字,且并不是所有拥有 8 位有效位的十进制小数都能够被准确保存?
    shintendo
        12
    shintendo  
       2021-11-10 13:29:22 +08:00
    @Higurashi
    8 位有效数字的,10000000 - 16777215 可以准确保存,16777216 - 99999999 不能准确保存,见 6 楼
    shintendo
        13
    shintendo  
       2021-11-10 14:19:33 +08:00
    @Higurashi
    我发现我好像也误解了你的问题,你问的是二进制转十进制这个过程产生的精度问题?
    Higurashi
        14
    Higurashi  
    OP
       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
        15
    Higurashi  
    OP
       2021-11-10 21:22:38 +08:00
    @shintendo #13
    暂时只是十进制转二进制过程中的精度问题。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5505 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 47ms · UTC 09:02 · PVG 17:02 · LAX 02:02 · JFK 05:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.