//===-- Single-precision log2(x) function ---------------------------------===// // // 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 // //===----------------------------------------------------------------------===// #include "src/math/log2f.h" #include "common_constants.h" // Lookup table for (1/f) #include "src/__support/FPUtil/FEnvImpl.h" #include "src/__support/FPUtil/FPBits.h" #include "src/__support/FPUtil/PolyEval.h" #include "src/__support/FPUtil/except_value_utils.h" #include "src/__support/FPUtil/multiply_add.h" #include "src/__support/common.h" #include "src/__support/macros/config.h" #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY // This is a correctly-rounded algorithm for log2(x) in single precision with // round-to-nearest, tie-to-even mode from the RLIBM project at: // https://people.cs.rutgers.edu/~sn349/rlibm // Step 1 - Range reduction: // For x = 2^m * 1.mant, log2(x) = m + log2(1.m) // If x is denormal, we normalize it by multiplying x by 2^23 and subtracting // m by 23. // Step 2 - Another range reduction: // To compute log(1.mant), let f be the highest 8 bits including the hidden // bit, and d be the difference (1.mant - f), i.e. the remaining 16 bits of the // mantissa. Then we have the following approximation formula: // log2(1.mant) = log2(f) + log2(1.mant / f) // = log2(f) + log2(1 + d/f) // ~ log2(f) + P(d/f) // since d/f is sufficiently small. // log2(f) and 1/f are then stored in two 2^7 = 128 entries look-up tables. // Step 3 - Polynomial approximation: // To compute P(d/f), we use a single degree-5 polynomial in double precision // which provides correct rounding for all but few exception values. // For more detail about how this polynomial is obtained, please refer to the // papers: // Lim, J. and Nagarakatte, S., "One Polynomial Approximation to Produce // Correctly Rounded Results of an Elementary Function for Multiple // Representations and Rounding Modes", Proceedings of the 49th ACM SIGPLAN // Symposium on Principles of Programming Languages (POPL-2022), Philadelphia, // USA, Jan. 16-22, 2022. // https://people.cs.rutgers.edu/~sn349/papers/rlibmall-popl-2022.pdf // Aanjaneya, M., Lim, J., and Nagarakatte, S., "RLibm-Prog: Progressive // Polynomial Approximations for Fast Correctly Rounded Math Libraries", // Dept. of Comp. Sci., Rutgets U., Technical Report DCS-TR-758, Nov. 2021. // https://arxiv.org/pdf/2111.12852.pdf. namespace LIBC_NAMESPACE_DECL { LLVM_LIBC_FUNCTION(float, log2f, (float x)) { using FPBits = typename fputil::FPBits; FPBits xbits(x); uint32_t x_u = xbits.uintval(); // Hard to round value(s). using fputil::round_result_slightly_up; int m = -FPBits::EXP_BIAS; // log2(1.0f) = 0.0f. if (LIBC_UNLIKELY(x_u == 0x3f80'0000U)) return 0.0f; // Exceptional inputs. if (LIBC_UNLIKELY(x_u < FPBits::min_normal().uintval() || x_u > FPBits::max_normal().uintval())) { if (x == 0.0f) { fputil::set_errno_if_required(ERANGE); fputil::raise_except_if_required(FE_DIVBYZERO); return FPBits::inf(Sign::NEG).get_val(); } if (xbits.is_neg() && !xbits.is_nan()) { fputil::set_errno_if_required(EDOM); fputil::raise_except(FE_INVALID); return FPBits::quiet_nan().get_val(); } if (xbits.is_inf_or_nan()) { return x; } // Normalize denormal inputs. xbits = FPBits(xbits.get_val() * 0x1.0p23f); m -= 23; } m += xbits.get_biased_exponent(); int index = xbits.get_mantissa() >> 16; // Set bits to 1.m xbits.set_biased_exponent(0x7F); float u = xbits.get_val(); double v; #ifdef LIBC_TARGET_CPU_HAS_FMA v = static_cast(fputil::multiply_add(u, R[index], -1.0f)); // Exact. #else v = fputil::multiply_add(static_cast(u), RD[index], -1.0); // Exact #endif // LIBC_TARGET_CPU_HAS_FMA double extra_factor = static_cast(m) + LOG2_R[index]; // Degree-5 polynomial approximation of log2 generated by Sollya with: // > P = fpminimax(log2(1 + x)/x, 4, [|1, D...|], [-2^-8, 2^-7]); constexpr double COEFFS[5] = {0x1.71547652b8133p0, -0x1.71547652d1e33p-1, 0x1.ec70a098473dep-2, -0x1.7154c5ccdf121p-2, 0x1.2514fd90a130ap-2}; double vsq = v * v; // Exact double c0 = fputil::multiply_add(v, COEFFS[0], extra_factor); double c1 = fputil::multiply_add(v, COEFFS[2], COEFFS[1]); double c2 = fputil::multiply_add(v, COEFFS[4], COEFFS[3]); double r = fputil::polyeval(vsq, c0, c1, c2); return static_cast(r); } } // namespace LIBC_NAMESPACE_DECL