马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
媒介
小提示:阅读本篇内容,至少必要相识double和float的二进制表示规则。
书中的代码示比方下:- #include <stdio.h>
- int main(void)
- {
- float a,b;
- b = 2.0e20 + 1.0;
- a = b - 2.0e20;
- printf("%f \n",a);
- return 0;
- }
复制代码 我的测试环境如下所示,在该测试环境中,a 等于 4008175468544.000000。- Linux version 5.15.0-134-generic (buildd@lcy02-amd64-092) (gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #145~20.04.1-Ubuntu SMP Mon Feb 17 13:27:16 UTC 2025
复制代码 为什么会产生这样的结果呢?由于书中的解释不够详细,以是我在阅读至此处时,天然而然产生了想要深入研究的想法,遂将研究结果写为本篇博客,供大家参考。
针对这个问题,我的思路是,逐行分析每一句代码都产生了什么结果,大概就可得知误差出现在那里。下面开始逐行分析。
逐行分析解读-第一行代码
起首是第一行:先从右边的盘算 2.0e20 + 1.0 开始,我们起首必要把这两个常量的二进制表示出来,然后做浮点数加法运算。
2.0e20 和 1.0的二进制表示
起首,2.0e20会被当作double类型的数据举行处理,根据double类型的存储规则,它的底层二进制如下:
0-10001000010-0101101011110001110101111000101101011000110001000000
其中:
- 符号位:0
- 指数部门:10001000010
- 尾数部门:0101101011110001110101111000101101011000110001000000
插播一个小知识(不关心的可略过):给定一个大数,它的二进制是怎样盘算得出的呢?
- 起首,将\(2 \times 10^{20}\)举行质因式分解:
\(2 \times 10^{20} = 2 \times (2 \times 5)^{20} = 2^{21} \times 5^{20}\)
这表明,\(2 \times 10^{20}\)是由\(2^{21}\)和\(5^{20}\)的乘积构成,也就是说,这个大数的二进制形式可以看成5²⁰的二进制左移21位(即乘以2²¹),也就是说,我们必要求得\(5^{20}\)的二进制。
\(5^{20}\)的十进制为:95367431640625
十进制求得二进制的过程,简单来说,即用95367431640625不断除以2,得到余数1或者余数0的一个竖向序列,将其倒序便是所求二进制。这里我直接写出二进制:
10101101011110001110101111000101101011000110001(一共为47位)
由上述盘算可得知,\(2 \times 10^{20}\)的二进制为:
\(10101101011110001110101111000101101011000110001 \times 2^{21}\) 我们将其规范化,得到
\(1.0101101011110001110101111000101101011000110001 \times 2^{67}\)
- 求出了整体的二进制,double类型的具体存储就简单很多了。
起首是指数部门:67 + 1023 = 1090,二进制为10001000010
然后是尾数部门:直接提取小数点之后的部门,只有46位,还必要填充6个0即可(满意尾数52位的需求)
(小知识后咱们继续)其次,1.0也会被当作double类型来接收,底层二进制如下:
0 01111111111 0000000000000000000000000000000000000000000000000000
这个二进制就不详细阐明了,相信有了前文的基础,读者可以轻松盘算出来。
这里提供一套工具函数,用来打印float和double类型的二进制bit,用来验证很方便:
[code]#include #include void print_bits_float(float f){ union { float a; uint32_t b; }c; c.a = f; for (int i = 0; i < 32; i++) { printf("%d", c.b & 1 more than 31 is undefined behaviour. printf("%d", c.b & (1 ULL |