【发布时间】: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)。
澄清:我正在寻找适用于 x 和 y 是浮点数的答案。 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<double>(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