Когда использовать `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, это гарантирует, что промежуточные вычисления не будут переполняться.

Вы можете увидеть пример этого несоответствия здесь.

Если вы работаете с номерами, которые, как вы знаете, не будут переполнять соответствующее представление для вашей платформы, вы можете с радостью использовать наивную версию.