21 changes: 21 additions & 0 deletions libc/test/src/math/ldexpf_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Unittests for ldexpf ----------------------------------------------===//
//
// 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 "LdExpTest.h"

#include "include/math.h"
#include "src/math/ldexpf.h"
#include "utils/CPP/Functional.h"
#include "utils/FPUtil/FPBits.h"
#include "utils/FPUtil/ManipulationFunctions.h"
#include "utils/FPUtil/TestHelpers.h"
#include "utils/UnitTest/Test.h"

#include <limits.h>

LIST_LDEXP_TESTS(float, __llvm_libc::ldexpf)
21 changes: 21 additions & 0 deletions libc/test/src/math/ldexpl_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Unittests for ldexpl ----------------------------------------------===//
//
// 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 "LdExpTest.h"

#include "include/math.h"
#include "src/math/ldexpl.h"
#include "utils/CPP/Functional.h"
#include "utils/FPUtil/FPBits.h"
#include "utils/FPUtil/ManipulationFunctions.h"
#include "utils/FPUtil/TestHelpers.h"
#include "utils/UnitTest/Test.h"

#include <limits.h>

LIST_LDEXP_TESTS(long double, __llvm_libc::ldexpl)
24 changes: 24 additions & 0 deletions libc/utils/FPUtil/ManipulationFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,30 @@ static inline T logb(T x) {
return normal.exponent;
}

template <typename T,
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
static inline T ldexp(T x, int exp) {
FPBits<T> bits(x);
if (bits.isZero() || bits.isInfOrNaN() || exp == 0)
return x;

// NormalFloat uses int32_t to store the true exponent value. We should ensure
// that adding |exp| to it does not lead to integer rollover. But, we |exp|
// value is larger the exponent range for type T, then we can return infinity
// early.
if (exp > FPBits<T>::maxExponent)
return bits.sign ? FPBits<T>::negInf() : FPBits<T>::inf();

// Similarly on the negative side.
if (exp < -FPBits<T>::maxExponent)
return bits.sign ? FPBits<T>::negZero() : FPBits<T>::zero();

// For all other values, NormalFloat to T conversion handles it the right way.
NormalFloat<T> normal(bits);
normal.exponent += exp;
return normal;
}

} // namespace fputil
} // namespace __llvm_libc

Expand Down
65 changes: 50 additions & 15 deletions libc/utils/FPUtil/NormalFloat.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,30 +93,47 @@ template <typename T> struct NormalFloat {
// Max exponent is of the form 0xFF...E. That is why -2 and not -1.
constexpr int maxExponentValue = (1 << ExponentWidth<T>::value) - 2;
if (biasedExponent > maxExponentValue) {
// TODO: Should infinity with the correct sign be returned?
return FPBits<T>::buildNaN(1);
return sign ? FPBits<T>::negInf() : FPBits<T>::inf();
}

FPBits<T> result(T(0.0));
result.sign = sign;

constexpr int subnormalExponent = -FPBits<T>::exponentBias + 1;
if (exponent < subnormalExponent) {
unsigned shift = subnormalExponent - exponent;
if (shift <= MantissaWidth<T>::value) {
// Since exponent > subnormalExponent, shift is strictly greater than
// zero.
if (shift <= MantissaWidth<T>::value + 1) {
// Generate a subnormal number. Might lead to loss of precision.
// We round to nearest and round halfway cases to even.
const UIntType shiftOutMask = (UIntType(1) << shift) - 1;
const UIntType shiftOutValue = mantissa & shiftOutMask;
const UIntType halfwayValue = UIntType(1) << (shift - 1);
result.exponent = 0;
result.mantissa = mantissa >> shift;
result.sign = sign;
UIntType newMantissa = result.mantissa;
if (shiftOutValue > halfwayValue) {
newMantissa += 1;
} else if (shiftOutValue == halfwayValue) {
// Round to even.
if (result.mantissa & 0x1)
newMantissa += 1;
}
result.mantissa = newMantissa;
// Adding 1 to mantissa can lead to overflow. This can only happen if
// mantissa was all ones (0b111..11). For such a case, we will carry
// the overflow into the exponent.
if (newMantissa == one)
result.exponent = 1;
return result;
} else {
// TODO: Should zero with the correct sign be returned?
return FPBits<T>::buildNaN(1);
return result;
}
}

result.exponent = exponent + FPBits<T>::exponentBias;
result.mantissa = mantissa;
result.sign = sign;
return result;
}

Expand Down Expand Up @@ -192,32 +209,50 @@ template <> inline NormalFloat<long double>::operator long double() const {
// Max exponent is of the form 0xFF...E. That is why -2 and not -1.
constexpr int maxExponentValue = (1 << ExponentWidth<long double>::value) - 2;
if (biasedExponent > maxExponentValue) {
// TODO: Should infinity with the correct sign be returned?
return FPBits<long double>::buildNaN(1);
return sign ? FPBits<long double>::negInf() : FPBits<long double>::inf();
}

FPBits<long double> result(0.0l);
result.sign = sign;

constexpr int subnormalExponent = -FPBits<long double>::exponentBias + 1;
if (exponent < subnormalExponent) {
unsigned shift = subnormalExponent - exponent;
if (shift <= MantissaWidth<long double>::value) {
if (shift <= MantissaWidth<long double>::value + 1) {
// Generate a subnormal number. Might lead to loss of precision.
// We round to nearest and round halfway cases to even.
const UIntType shiftOutMask = (UIntType(1) << shift) - 1;
const UIntType shiftOutValue = mantissa & shiftOutMask;
const UIntType halfwayValue = UIntType(1) << (shift - 1);
result.exponent = 0;
result.mantissa = mantissa >> shift;
result.implicitBit = 0;
result.sign = sign;
UIntType newMantissa = result.mantissa;
if (shiftOutValue > halfwayValue) {
newMantissa += 1;
} else if (shiftOutValue == halfwayValue) {
// Round to even.
if (result.mantissa & 0x1)
newMantissa += 1;
}
result.mantissa = newMantissa;
// Adding 1 to mantissa can lead to overflow. This can only happen if
// mantissa was all ones (0b111..11). For such a case, we will carry
// the overflow into the exponent and set the implicit bit to 1.
if (newMantissa == one) {
result.exponent = 1;
result.implicitBit = 1;
} else {
result.implicitBit = 0;
}
return result;
} else {
// TODO: Should zero with the correct sign be returned?
return FPBits<long double>::buildNaN(1);
return result;
}
}

result.exponent = biasedExponent;
result.mantissa = mantissa;
result.implicitBit = 1;
result.sign = sign;
return result;
}
#endif
Expand Down