blob: c92a700de15334901a0c153af46742c608426723 [file]
//===-- Implementation header for hypot -------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_HYPOT_H
#define LLVM_LIBC_SRC___SUPPORT_MATH_HYPOT_H
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/Hypot.h"
#include "src/__support/FPUtil/double_double.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/sqrt.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
namespace LIBC_NAMESPACE_DECL {
namespace math {
LIBC_INLINE double hypot(double x, double y) {
using FPBits = fputil::FPBits<double>;
using DoubleDouble = fputil::DoubleDouble;
uint64_t x_u = FPBits(x).uintval();
uint64_t y_u = FPBits(y).uintval();
// Shift the exponent field to the top 11 bits of the lower 32-bit.
// Casting it to 32-bit effectively remove the sign bit.
uint32_t x_e = static_cast<uint32_t>(x_u >> 31);
uint32_t y_e = static_cast<uint32_t>(y_u >> 31);
// a = maximum_mag(x, y);
// b = minimum_mag(x, y);
double a, b;
uint32_t a_e, b_e;
if (x_e >= y_e) {
a_e = x_e;
b_e = y_e;
a = x;
b = y;
} else {
a_e = y_e;
b_e = x_e;
a = y;
b = x;
}
double scale = 1.0;
double scale_back = 1.0;
// For a_e, b_e, the top 11 bits are exponent fields.
if (LIBC_UNLIKELY(a_e >= ((500U + FPBits::EXP_BIAS) << (32 - 11)))) {
// The larger magnitude is above 2^500 (or Inf/NaN), need to scale down to
// prevent overflow when squaring.
if (a_e >= static_cast<uint32_t>(FPBits::EXP_MASK >> 31)) {
// Inf or NaN;
FPBits x_bits(x);
FPBits y_bits(y);
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
if (x_bits.is_inf() || y_bits.is_inf())
return FPBits::inf().get_val();
if (x_bits.is_nan())
return x;
return y;
}
// Any scaling factor < 2^(-1024/2) = 2^-512 would work.
scale = 0x1.0p-600;
scale_back = 0x1.0p600;
a *= scale;
b *= scale;
} else if (LIBC_UNLIKELY(b_e <= ((FPBits::EXP_BIAS - 500) << (32 - 11)))) {
// The smaller magnitude is below 2^-500 (or 0), need to scale up to prevent
// underflow when squaring.
if ((x == 0.0) || (y == 0.0)) {
double x_abs = FPBits(x_u & FPBits::EXP_SIG_MASK).get_val();
double y_abs = FPBits(y_u & FPBits::EXP_SIG_MASK).get_val();
return x_abs + y_abs;
}
// Any scaling factor > 2^((1072 + 52)/2) = 2^562 would work.
scale = 0x1.0p600;
scale_back = 0x1.0p-600;
a *= scale;
b *= scale;
}
// When the gap in the exponent of `a` and `b` is >= 54,
// |b| < ufp(a) * 2^(-53) = ulp(a)/2
// So:
// hypot(x, y) = sqrt(a^2 + b^2)
// <= sqrt( (|a| + |b|)^2 )
// = |a| + |b|
// < |a| + ulp(a)
// Hence, we can return:
// |a| + |b| = |x| + |y|
// to perform correct rounding to all rounding modes.
if (LIBC_UNLIKELY(a_e - b_e >= (54U << (32 - 11)))) {
double x_abs = FPBits(x_u & FPBits::EXP_SIG_MASK).get_val();
double y_abs = FPBits(y_u & FPBits::EXP_SIG_MASK).get_val();
return x_abs + y_abs;
}
// sum.hi + sum.lo ~ a^2 + b^2.
DoubleDouble a_sq = fputil::exact_mult(a, a);
DoubleDouble b_sq = fputil::exact_mult(b, b);
DoubleDouble sum = fputil::exact_add(a_sq.hi, b_sq.hi);
sum.lo += a_sq.lo + b_sq.lo;
// Let hi = sum.hi and lo = sum.lo.
// To compute r_hi + r_lo ~ sqrt(hi + lo):
// - First we use fast sqrt instruction to get:
// r_hi ~ sqrt(hi)
// - Then use Taylor expansion:
// f(hi + lo) = f(hi) + f'(hi) * lo + f''(hi) * lo^2 / 2 + ...
// with f(x) = sqrt(x):
// sqrt(hi + lo) ~ sqrt(hi) + lo / (2 * sqrt(hi)).
// - Subtract by r_hi to find the correction term:
// sqrt(hi + lo) - r_hi ~ (sqrt(hi) - r_hi) + lo / (2 * sqrt(hi))
// - Instead of finding the rounding errors sqrt(hi) - r_hi, we use the
// squared residual d = hi - r_hi^2, which can be calculated accurately in
// double-double. Then, using the same Taylor approximation of sqrt(x) as
// above:
// sqrt(hi) - r_hi = sqrt(r_hi^2 + d) - r_hi
// ~ sqrt(r_hi^2) + d / (2 * sqrt(r_hi^2)) - r_hi
// = d / (2 * r_hi).
// - Similarly,
// 1 / sqrt(hi) = 1 / sqrt(r_hi^2 + d)
// ~ 1 / sqrt(r_hi^2) - d / (2 * (r_hi^2)^(3/2))
// = 1 / r_hi - d / (2 * r_hi^3)
// - Putting them together, we have the correction term:
// sqrt(hi + lo) - r_hi + lo / (2 * sqrt(hi)) ~
// ~ (lo + d) / (2 * r_hi) + lo * d / (4 * r_hi^3)
// ~ (hi + lo - r_hi^2) / (2 * r_hi).
// - When computing hi + lo - r_hi^2, we will pair (hi - r_sq.hi) and
// (lo - r_sq.lo), since `r_sq.hi` is very close to `hi`, and the
// subtraction is exact.
// - Taking intermediate roundings with directed rounding modes into
// consideration, the overall errors should be bounded by
// (2^-51)^2 = 2^-102.
// |sqrt(sum.hi) - r_hi| < 2^-52.
double r_hi = fputil::sqrt<double>(sum.hi);
// r_inv ~ 1 / (2 * r_hi)
double r_inv = 0.5 / r_hi;
// r_hi^2
DoubleDouble r_sq = fputil::exact_mult(r_hi, r_hi);
// (hi + lo - r_hi^2)
double num_lo = (sum.lo - r_sq.lo) - (r_sq.hi - sum.hi);
// (hi + lo - r_hi^2) / (2 * r_hi)
double r_lo = num_lo * r_inv;
#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
// TODO: What's the worst error if we just do:
// return sqrt(a*a + b*b) * scale_back;
// without all the double-double computations?
return (r_hi + r_lo) * scale_back;
#else
constexpr double ERR = 0x1.0p-102;
// Ziv's rounding test.
double upper = r_hi + fputil::multiply_add(r_hi, ERR, r_lo);
double lower = r_hi + fputil::multiply_add(r_hi, -ERR, r_lo);
if (LIBC_LIKELY(upper == lower)) {
return upper * scale_back;
}
return fputil::hypot(x, y);
#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
}
} // namespace math
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_MATH_HYPOT_H