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
为浮点数的答案。例如比较:
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页面中所示。 - phuclvx*x + y*y
可能会失去一些精度。这意味着std::sqrt(x*x+y*y)
可能会有一两个误差。需要使用比std::sqrt(x*x+y*y)
更好的算法才能获得该保证。(续) - David Hammenhypot
必须设置舍入以达到这种精度,然后将舍入恢复回您的设置。这种设置和重置舍入行为是std:hypot(x,y)
比std::sqrt(x*x+y*y)
慢得多的原因。 - David Hammen=sqrt(x)*(1+e/2) - (而平方会使其加倍),因此从上面看起来平方根方法并不像它看起来那么糟糕。 hypot的额外运行时间部分是由于选择不同的方法以获得额外的精度和避免溢出/下溢的步骤,但也包括inf的特殊测试(例如,hypot(inf,NaN) -> inf,而另一种方法会给你NaN)。 - greggo