浮点数的坑
0.0 与 -0.0 不同
在浮点数中,double
类型的0.0
用0x0000000000000000
表示,-0.0
用0x8000000000000000
表示;float
类型的0.0
用0x00000000
表示,-0.0
用0x80000000
表示;
所以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输出浮点数时默认使用四舍六入五留双的数值修约法。
也就是说,