【问题标题】:When to use `std::hypot(x,y)` over `std::sqrt(x*x + y*y)`何时使用 `std::hypot(x,y)` 而不是 `std::sqrt(x*x + y*y)`
【发布时间】:2015-12-02 20:09:27
【问题描述】:

documentation of 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 是浮点数的答案。 IE。比较:

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);

【问题讨论】:

  • 我认为您还应该将其与std::abs(std::complex&lt;double&gt;(x,y)) 进行比较,如std::hypot 页面
  • 晚了,但是 cppreference 文档也作为注释说(因此标准不能保证)“实现通常保证精度小于 1 ulp(最后一个单位)。” x*x + y*y 可能会丢失几位精度,如果使用四舍五入到最接近的集合。这意味着std::sqrt(x*x+y*y) 可以关闭一两点。需要比std::sqrt(x*x+y*y) 更好的算法来获得该保证。 (续)
  • 更糟糕的是,假设你已经搞砸了四舍五入?这肯定会妨碍实现亚 ulp 精度。 hypot 必须设置舍入以达到该精度,然后将舍入恢复为您的设置。这种舍入行为的设置和重置使std:hypot(x,y)std::sqrt(x*x+y*y) 慢得多。
  • 我很喜欢这个问题,但我仍然想知道为什么会出现性能差异。 stackoverflow.com/questions/3764978/… 对此进行了讨论。具体来说,stackoverflow.com/a/3764993/725805 为我解释了它。
  • sqrt 函数的特性是输入中存在的任何相对误差都会在平方根的结果中减半 --- 即 sqrt(x*(1+e)) ~=~ sqrt( x)*(1+e/2) --- (而平方是两倍),所以平方根方法并不像上面看起来的那么糟糕。 hypot 的额外运行时间部分是由在不同的方法中选择以获得额外的精度,以及避免上溢/下溢的步骤,以及对 inf 的特殊测试(例如 hypot(inf,NaN) -> inf,而另一种方法给出你南)。

标签: c++ c++11 floating-accuracy


【解决方案1】:

答案在您引用的文档中

计算 x 和 y 的平方和的平方根,在计算的中间阶段不会出现过度上溢或下溢

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

您可以查看此差异的示例here

如果您使用的数字不会超出您平台的相关表示,您可以愉快地使用幼稚版本。

【讨论】:

  • std::hypot的参数是浮点类型,所以如果你在这里使用int也会被提升为浮点。
  • 我的问题可能不是 100% 清楚。当使用double 精度参数时,我只对两者之间的区别感兴趣。因为整数溢出是由类型提升处理的,而不是 hypot 函数。但是hypot 中的某些内容使其比天真的实现慢了 20 倍(即使使用类型提升)。为什么会这样?什么时候能救我的牙?
  • 可以看出in the example here 使用双精度参数时没有差异。具体来说,我正在寻找会出现差异的double x,y
  • @EmilyL。只需将值设为large enough
  • @EmilyL。对于给定的平台,当然。更一般地说,当它超过std::numeric_limits&lt;double&gt;::max()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-22
  • 2020-01-04
  • 1970-01-01
相关资源
最近更新 更多