C++浮点数精度问题
C++默认有效位数
C++ 默认有效位数为 6 位,指数位和小数位共享。超过有效位数时,只输出前 6 位,且第六位四舍五入运算。
cout << 12345.54555 << "\n"; // 输出 12345.5
cout << 12345.55555 << "\n"; // 输出 12345.6,被四舍五入了
cout << 123456.45555 << "\n"; // 输出 123456
cout << 123456.55555 << "\n"; // 输出 123457,被四舍五入了
当 整数位超过有效位数后,自动变为科学计数法输出。
cout << 1234567.12345 << "\n"; // 输出 1.23457e+06
cout << 12345678901234567890.12345 << "\n"; // 输出 1.23457e+19
fixed函数
该函数用于切换有效位数的判断逻辑,未使用时指数位和小数位共享有效位数,使用后变为小数位独享。
cout << fixed << 123456.1234564 << "\n"; // 输出 123456.123456
cout << fixed << 123456.1234565 << "\n"; // 输出 123457.123457,被四舍五入了
setprecison函数
该函数用于修改有效位数,单独使用时整数位和小数位共享修改后的有效位数,与 fixed
配合时变为小数位独享。
cout << setprecision(10) << 123456.123456; // 输出 123456.1235
cout << fixed << setprecision(10) << 123456.123456; // 输出 123456.1234560000
函数的返回类型与隐式转换1
这一问题在 pow
函数的使用过程中非常常见。众所周知,pow
函数会返回一个浮点数类型的答案,这就导致令人头疼的精度问题会再次出现。
double p = pow(8, 1.0 / 3);
double q = pow(216, 1.0 / 3);
cout << fixed << setprecision(12) << p << "\n"; // 输出 2.000000000000
cout << fixed << setprecision(20) << p << "\n"; // 输出 2.00000000000000000000
cout << fixed << setprecision(12) << q << "\n"; // 输出 6.000000000000
cout << fixed << setprecision(20) << q << "\n"; // 输出 5.99999999999999911182
如果这一问题配合上隐式转换,就会产生灾难(备注:\(\sqrt[3]{216}=6\),由于 pow
得到的是 5.99999… ,隐式转换将小数部分全部舍去后输出 5 )。
double q = pow(216, 1.0 / 3);
cout << (int)q << "\n"; // 输出 5
注:如果需要开立方根,C++库中自带函数 cbrt
,用法与 sqrt
一致,且两者都保证精确。
函数的返回类型与隐式转换2
double与int比较
int num1 = 0, num2 = 0;
for (int i = 1; i <= 100; i++) {
if (sqrt(i) * sqrt(i) == i) {
num1++;
}
if ((int)sqrt(i) * (int)sqrt(i) == i) {
num2++;
}
}
cout << num1 << "\n"; // 输出 49,答案错误!
cout << num2 << "\n"; // 输出 10,答案正确
浮点注意事项
(1)某些函数并不支持浮点数传入,例如 minmax
函数,翻阅库之后我们可以发现,其只支持传入整数/字符串列表;
(2)浮点数整数和小数部分共享精度,所以在计算时,整数部分越大,小数部分精度越差。一般我们默认在数据集超过 10^6 时不再使用浮点数进行运算,否则会出现很严重的精度误差。
(3)由于浮点数四则运算存在严重误差,故一般我们默认不直接对浮点数进行大小比较、也不直接对浮点数进行符号判断。
卡精度常用方法
//结果保留4位小数
double res=1.1111;
res*=10000;
res=(int)res;
res/=10000;
if(check(res)符合条件) res+=1e-4;