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)由于浮点数四则运算存在严重误差,故一般我们默认不直接对浮点数进行大小比较、也不直接对浮点数进行符号判断。

热门相关:宝贝迷人,总裁圈住爱   仙碎虚空   睡服BOSS:老公,躺下!   年轻的女仆   年轻的阿姨3