浮点数的坑


浮点数的坑

0.0 与 -0.0 不同

在浮点数中,double类型的0.00x0000000000000000表示,-0.00x8000000000000000表示;float类型的0.00x00000000表示,-0.00x80000000表示;

所以printf("%.1f", -0.0)会输出-0.0并不是0.0

double d1 = 0.0;
double d2 = -0.0;
float f1 = 0.0f;
float f2 = -0.0f;
unsigned long long* p1 = &d1;
unsigned long long* p2 = &d2;
unsigned int* p3 = &f1;
unsigned int* p4 = &f2;

printf("d1 = %f (0x%016llx)\n", d1, *p1);
printf("d2 = %f (0x%016llx)\n", d2, *p2);
printf("f1 = %f (0x%08x)\n", f1, *p3);
printf("f2 = %f (0x%08x)\n", f2, *p4);

输出:

d1 = 0.000000 (0x0000000000000000)         
d2 = -0.000000 (0x8000000000000000)        
f1 = 0.000000 (0x00000000)                 
f2 = -0.000000 (0x80000000) 

0.1+0.2 == 0.3 输出 false

不是所有小数都可以用「完整」的二进制来表示的,比如十进制 0.1 在转换成二进制小数的时候,是一串无限循环的二进制数,计算机是无法表达无限循环的二进制数的,毕竟计算机的资源是有限。

因此,计算机只能用「近似值」来表示该二进制,那么意味着计算机存放的小数可能不是一个真实值。

现在基本都是用 IEEE 754 规范的「单精度浮点类型」或「双精度浮点类型」来存储小数的,根据精度的不同,近似值也会不同。

0.1 的二进制浮点数转换成十进制的结果是 0.100000001490116119384765625

0.2 的二进制浮点数转换成十进制的结果是 0.20000000298023223876953125

这两个结果相加就是 0.300000004470348358154296875

所以,你会看到在计算机中 0.1 + 0.2 并不等于完整的 0.3

double a = 0.1;
double b = 0.2;
double ab = a+b;
double c = 0.3;
printf("0.1: %.30f\n", a);
printf("0.2: %.30f\n", b);
printf("0.3: %.30f\n", c);
printf("0.1+0.2: %.30f\n", ab);

输出:

0.1: 0.100000000000000005551115123126    
0.2: 0.200000000000000011102230246252    
0.3: 0.299999999999999988897769753748    
0.1+0.2: 0.300000000000000044408920985006

输出数据与输入数据不一致

比如:单精度浮点赋值20.3,然后将该浮点打印输出,代码及打印结果如下,

float a=20.3f;
printf("%f\n",a)

为什么结果与预期不一样,就是因为20.3并不在浮点的数据集合中,编译器会选择最接近20.3的一个数据,这个数据的编码为0x41a26666,转化为10进制为20.2999992。

解决办法:通过四舍五入,保证有效位以内的10进制有效数字,代码及打印结果如下所示:

float a=20.3f;
a=a+0.00005;
printf("%.4f\n",a)

printf("%.0f", 4.5) 输出4 而 printf("%.0f", 5.5)输出6

printf输出浮点数时默认使用四舍六入五留双的数值修约法。

也就是说,

  • 小于等于4时舍去
  • 大于等于6时进入
  • 等于5时而且5后面仍有数,则无论奇偶都要进入。
  • 等于5时而且5后面不再有数前一位决定是舍去还是进入: 如果是奇数则进入,如果是偶数则舍去。

文章作者: Vinx
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Vinx !
 上一篇
STM32 使用 CLion 时SystemClock_Config失败 STM32 使用 CLion 时SystemClock_Config失败
STM32 使用 CLion 时SystemClock_Config失败 可能原因,来自https://blog.t123yh.xyz:3/index.php/archives/922 故障分析 我们从 OpenOCD 的启动流程
2023-11-15
下一篇 
ESP_IDF 二进制文件各个段说明 ESP_IDF 二进制文件各个段说明
ESP_IDF 二进制文件各个段说明 DRAM .data 所有初始化值为非零值的静态变量 分配 D/IRAM 空间 还在二进制映像中占用空间来存储非零初始化值。
2023-08-31
  目录