何时使用' std::hypot(x,y) '而不是' std::sqrt(x*x + y*y) '

When to use `std::hypot(x,y)` over `std::sqrt(x*x + y*y)`

本文关键字:std sqrt hypot 何时使      更新时间:2023-10-16

std::hypot的文档说:

计算x和y平方和的平方根,在计算的中间阶段没有不当的溢出或下溢。

我很难设想一个测试用例,其中std::hypot应该在平凡的sqrt(x*x + y*y)上使用。

下面的测试显示std::hypot大约比原始计算慢20倍。

#include <iostream>
#include <chrono>
#include <random>
#include <algorithm>
int main(int, char**) {
    std::mt19937_64 mt;
    const auto samples = 10000000;
    std::vector<double> values(2 * samples);
    std::uniform_real_distribution<double> urd(-100.0, 100.0);
    std::generate_n(values.begin(), 2 * samples, [&]() {return urd(mt); });
    std::cout.precision(15);
    {
        double sum = 0;
        auto s = std::chrono::steady_clock::now();
        for (auto i = 0; i < 2 * samples; i += 2) {
            sum += std::hypot(values[i], values[i + 1]);
        }
        auto e = std::chrono::steady_clock::now();
        std::cout << std::fixed <<std::chrono::duration_cast<std::chrono::microseconds>(e - s).count() << "us --- s:" << sum << std::endl;
    }
    {
        double sum = 0;
        auto s = std::chrono::steady_clock::now();
        for (auto i = 0; i < 2 * samples; i += 2) {
            sum += std::sqrt(values[i]* values[i] + values[i + 1]* values[i + 1]);
        }
        auto e = std::chrono::steady_clock::now();
        std::cout << std::fixed << std::chrono::duration_cast<std::chrono::microseconds>(e - s).count() << "us --- s:" << sum << std::endl;
    }
}

所以我要求指导,什么时候我必须使用std::hypot(x,y)来获得比更快的std::sqrt(x*x + y*y)正确的结果。

澄清:我正在寻找xy是浮点数时适用的答案。即比较:

double h = std::hypot(static_cast<double>(x),static_cast<double>(y));

:

double xx = static_cast<double>(x);
double yy = static_cast<double>(y);
double h = std::sqrt(xx*xx + yy*yy);

答案在你引用的文档中

计算x和y平方和的平方根,在计算的中间阶段没有过度的溢出或下溢

如果x*x + y*y溢出,那么如果您手动执行计算,您将得到错误的答案。但是,如果使用std::hypot,则可以保证中间计算不会溢出。

你可以在这里看到一个差异的例子。

如果您正在处理的数字不会超出您的平台的相关表示,那么您可以愉快地使用朴素版本。