为什么浮点类型存在误差
带有小数的表示:
25.3
整数通过除2取余法表示:
25/2…1
12/2…0
6/2…0
3/2…1
1/2…1
倒过来:25(十进制)= 11001(二进制)
小数部分通过乘2取整法:
0.3 * 2 = 0.6 …0
0.6 * 2=1.2…1
0.22=0.4…0
0.42=0.8…0
0.82=1.6…1
0.62=1.2…1
0.22=0.4…0
0.42=0.8…0
0.82=1.6…1
0.62=1.2…1
0.22=0.4…0
0.42=0.8…0
…
再往下就是0011不断的重复,这说明了,用二进制表示小数,存在不能完全准确表示的情况,只能无限的逼近,换句话说就是十进制的小数太小了,导致一些小数无法用二进制完全准确的表示了,只能用近似值表示,这就是浮点类型存在误差的原因。
假设有12位的存储空间的话,上面只能表示为:0.010011001100
换成十进制数就是:
02-1
02-1+12-2+02-3+02-4+12-5+12-6+02-7+02-8+12-9+12-10+02-11+0*2-12
=0+1/4+0+0+1/32+1/64+1/512+1/1024
=0.25+0.03125+0.015625+0.001953125+0.0009765625
=0.2998046875
这个数就是浮点数表示的0.3,所以说,是由于小数在十进制转化到二进制时,存在无法完全转化,即存在循环的情况,导致了其不能完全准确的用二进制表示十进制数。
这就决定了,浮点的小数不能准确的表示,根因就是十进制小数存在不能完全转化为二进制小数的问题。
当前,也存在可以准确转化的数,比如:
0.75
0.752=1.5…1
0.52=1.0…1
(0.11)2进制=(0.75)十进制
12-1+12-2
=0.75
在代码当中的情况
程序情况:
#include <iostream>
int main()
{
float f1 = 7.123456789;
float f2 = 7.123456875;
std::cout << (f1 != f2 ? "f1 f2 not same\n" : "f1 f2 same\n");
float g = 1.0 / 3.0;
double d = 1.0 / 3.0;
std::cout << (g == d ? "g d same\n" : "g d not same\n");
double d1 = 1.9*9;
double d2 = 17.1;
std::cout << (d1 == d2 ? "d1 d2 same\n" : "d1 d2 not same\n");
std::cout << "Hello World!\n";
std::string str = "8.211267";
std::string str2 = "8.211267";
double fC1 = 9999.25899999;
double fC2 = 9999.25899999;
double fA = atof(str.c_str())* fC1;
double fB = atof(str2.c_str())* fC2;
if (fA == fB)
{
std::cout << "equal "<<std::endl;
}
else
{
std::cout << "not equal "<<std::endl;
}
}
运行情况:
运行时内存情况:
f1与f2为什么不相等呢?
首先,由于十进制单精度浮点数的有效位数为7,两个前7位相等而后面不同的数有可能在计算机中表示为同一个浮点数,因而判断两数不等而失败!
为了避免这类问题,请统一使用double,而不要混用不同精度的浮点。对于C++来说,float已是昔日黄花,除了过渡一些C程序,在新编的程序中实在没有太大用处,因为double完全包含了它,
而且浮点运算在内部都是先化为double进行的,使用float还必须付出转换回来的时间开销,因此混进float只会添乱!!
d1与d2为什么不相等呢?
由于浮点数在计算机内实际上是一个近似表示,在手工计算看来为正确的结果,在计算机中运算未必能得出正确的结果。
因为浮点数的构成原理,决定了十进制数在转换为内部浮点数时,由无穷尾数而带来的不精确性。上面程序中d1和d2变量的值本应相等,却在计算机内部为不等。
1.9我们看着是1.9,实际内存存储的可不是1.9啊,是什么呢?,如下所示:
呵呵,竟然是:1.89999999999…
总结
所以,我们还得到另外一个重要的经验:使用浮点数进行相等(==)和不等(!=)比较的操作通常是有问题的。浮点数的相等比较,
一般总是使用两者相减的值是否落在0的邻域中来判断的。
d1,d2如果这样比,就会相等:
简单的说,就是浮点类型的数存在误差,通过两个数的差,是否落在0的邻域进行判断即可: