为何R代码中1加2^(-52)结果为1,2^(-52)加2^(-53)不为0?
双精度浮点数运算原理说明
我们日常使用的R默认采用IEEE 754标准的双精度浮点数存储数值,这种格式的有效精度为53个二进制位(1位隐含前导1 + 52位显式存储的尾数位),只能精确表示可以写成k * 2^n(k为小于2^53的整数,n为整数)的数值,其余数值都需要经过舍入才能存储。
为什么1 + 2^(-52)的运行结果为1
首先要明确双精度浮点数的加法执行流程:
- 对阶:将指数更小的数的尾数右移,直到两个数的指数保持一致,右移过程中超出尾数位的内容会暂时存在保护位中参与计算
- 尾数相加
- 结果舍入到符合双精度精度要求的可表示值
1的指数是0,2^(-52)的指数是-52,对阶时需要把后者的尾数右移52位,刚好落在尾数的最低有效位上。如果相加后得到的结果是1,通常是两种原因:
- 打印精度限制:R默认只展示前15位有效十进制数,
1 + 2^(-52)的实际值为1.0000000000000002,如果执行print(1 + 2^(-52), digits = 16)就能看到完整的非1结果 - 如果确实运算后等于1,一般是触发了就近舍入到偶数的默认规则:比如如果加的是
2^(-53),相加后的结果刚好落在1和1+2^(-52)两个可表示值的中点,就会舍入到偶数的那个值,也就是1。
为什么2^(-52) + 2^(-53)不会返回0
这两个数的量级非常接近,指数仅差1:2^(-52)的指数是-52,2^(-53)的指数是-53,对阶时只需要把后者的尾数右移1位,完全在53位有效精度的覆盖范围内,没有任何精度丢失。
相加后的结果是3 * 2^(-53),刚好属于双精度可以精确表示的数值,自然不会返回0。
和第一个案例的核心差异是:两个相加的数量级差很小,对阶不会导致有效位被移出精度范围,也就不会触发丢失数值的舍入。
浮点数舍入的发生时机
舍入操作只会发生在单步运算结束后,将结果存储为对应精度的浮点数时:
- 所有基础运算(加减乘除、开方、求余等)的执行过程中,都会用额外的保护位、粘滞位暂存超出精度范围的中间结果,不会在运算中途丢精度
- 单步运算的中间结果计算完成后,才会按照预设的舍入规则(默认是就近舍入到偶数),将中间值舍入为当前精度可表示的浮点数,再写入内存或寄存器。
内容的提问来源于stack exchange,提问作者user16461886
相关产品推荐
相关产品推荐

