使用和不使用命名空间 std 的 C++ 长双精度差异;

k-d*_*-dx 2 c++ std

我偶然发现了我无法解释的奇怪的 C++ 行为。

我试图在调整图像大小(保持其比例)以适应尽可能多的屏幕时计算图像的尺寸。x、y 变量是图像的尺寸,X、Y 变量是屏幕的尺寸。当结果维度不是整数时,我需要使用标准数学规则对它们进行舍入。

这个输入程序499999999999999999 10 1000000000000000000 19给出了(错误的)答案950000000000000000 19

#include <iostream>
#include <cmath>
#include <algorithm>

int main()
{
    long long x, y, X, Y;
    std::cin >> x >> y >> X >> Y;

    long double ratio = std::min((long double)X/x, (long double)Y/y);

    x = round(ratio*x);
    y = round(ratio*y);

    std::cout << x << " " << y << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,下面的代码(仅更改并从函数体中using namespace std;删除)给出了正确的答案。std::main949999999999999998 19

#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;

int main()
{
    long long x, y, X, Y;
    cin >> x >> y >> X >> Y;

    long double ratio = min((long double)X/x, (long double)Y/y);

    x = round(ratio*x);
    y = round(ratio*y);

    cout << x << " " << y << endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我正在使用g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0并编译该程序g++ -std=c++17 -Wall -Wextra -Wshadow

Inn*_*der 9

在第一种情况下,您double round(double)从全局名称空间调用函数,该名称空间由cmath标头拉​​入。

long double std::round(long double)在第二种情况下,您从命名空间调用重载函数std,因为您是using namespace std.

std::您可以通过在 前面添加来修复代码round,如下所示:

#include <iostream>
#include <cmath>
#include <algorithm>

int main()
{
    long long x, y, X, Y;
    std::cin >> x >> y >> X >> Y;

    auto ratio = std::min((long double)X / x, (long double)Y / y);

    x = std::round(ratio * x); // <-- add std:: here
    y = std::round(ratio * y); // <-- add std:: here

    std::cout << x << " " << y;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

更新:

为了解释这里发生的情况,当您round在第一种情况下调用时,在传递给函数之前ratio * x会隐式转换为double(只能存储 15 位有效数字)。

这会导致精度损失并导致您的案例出现意外结果。没有怪癖。