Когда использовать `std:: hypot (x, y)` над `std:: sqrt (x * x + y * y)`
Документация 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);
Ответы
Ответ 1
Ответ приведен в документации, которую вы указали
Вычисляет квадратный корень из суммы квадратов x и y, без чрезмерного переполнения или нижнего потока на промежуточных этапах вычисления.
Если x*x + y*y
переполняется, тогда, если вы выполните вычисление вручную, вы получите неправильный ответ. Однако если вы используете std::hypot
, это гарантирует, что промежуточные вычисления не будут переполняться.
Вы можете увидеть пример этого несоответствия здесь.
Если вы работаете с номерами, которые, как вы знаете, не будут переполнять соответствующее представление для вашей платформы, вы можете с радостью использовать наивную версию.