PR #1777: Avoid std::ldexp in `operator double(int128)`.
Imported from GitHub PR https://github.com/abseil/abseil-cpp/pull/1777
This patch replaces all instances of
std::ldexp(msb, 64)
with
msb * (2**64)
as it turns out that this optimization is not done by MSVC. Worse, it emited a function call with error checking, even if the int128 cannot hit the inf limitation.
Sadly even the constant `std::ldexp(1.0, 64)` is not inlined: https://gcc.godbolt.org/z/oGhGz77sx
Merge a21b1c952494944e51e12c62127a71480bc28695 into 878313658ed7cbef40635e2394b28ef0242072d8
Merging this change closes #1777
COPYBARA_INTEGRATE_REVIEW=https://github.com/abseil/abseil-cpp/pull/1777 from degasus:int128_t a21b1c952494944e51e12c62127a71480bc28695
PiperOrigin-RevId: 688968524
Change-Id: Id88cf38e241553f88bf4d97e7b001247dcd5599b
diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h
index 21f65a3..ae736b2 100644
--- a/absl/numeric/int128.h
+++ b/absl/numeric/int128.h
@@ -789,16 +789,20 @@
// Conversion operators to floating point types.
inline uint128::operator float() const {
- return static_cast<float>(lo_) + std::ldexp(static_cast<float>(hi_), 64);
+ // Note: This method might return Inf.
+ constexpr float pow_2_64 = 18446744073709551616.0f;
+ return static_cast<float>(lo_) + static_cast<float>(hi_) * pow_2_64;
}
inline uint128::operator double() const {
- return static_cast<double>(lo_) + std::ldexp(static_cast<double>(hi_), 64);
+ constexpr double pow_2_64 = 18446744073709551616.0;
+ return static_cast<double>(lo_) + static_cast<double>(hi_) * pow_2_64;
}
inline uint128::operator long double() const {
+ constexpr long double pow_2_64 = 18446744073709551616.0L;
return static_cast<long double>(lo_) +
- std::ldexp(static_cast<long double>(hi_), 64);
+ static_cast<long double>(hi_) * pow_2_64;
}
// Comparison operators.
diff --git a/absl/numeric/int128_have_intrinsic.inc b/absl/numeric/int128_have_intrinsic.inc
index 51e4b9d..216115a 100644
--- a/absl/numeric/int128_have_intrinsic.inc
+++ b/absl/numeric/int128_have_intrinsic.inc
@@ -170,27 +170,29 @@
// complement overwhelms the precision of the mantissa.
//
// Also check to make sure we don't negate Int128Min()
+ constexpr float pow_2_64 = 18446744073709551616.0f;
return v_ < 0 && *this != Int128Min()
? -static_cast<float>(-*this)
: static_cast<float>(Int128Low64(*this)) +
- std::ldexp(static_cast<float>(Int128High64(*this)), 64);
+ static_cast<float>(Int128High64(*this)) * pow_2_64;
}
inline int128::operator double() const {
// See comment in int128::operator float() above.
+ constexpr double pow_2_64 = 18446744073709551616.0;
return v_ < 0 && *this != Int128Min()
? -static_cast<double>(-*this)
: static_cast<double>(Int128Low64(*this)) +
- std::ldexp(static_cast<double>(Int128High64(*this)), 64);
+ static_cast<double>(Int128High64(*this)) * pow_2_64;
}
inline int128::operator long double() const {
// See comment in int128::operator float() above.
+ constexpr long double pow_2_64 = 18446744073709551616.0L;
return v_ < 0 && *this != Int128Min()
? -static_cast<long double>(-*this)
: static_cast<long double>(Int128Low64(*this)) +
- std::ldexp(static_cast<long double>(Int128High64(*this)),
- 64);
+ static_cast<long double>(Int128High64(*this)) * pow_2_64;
}
#endif // Clang on PowerPC
diff --git a/absl/numeric/int128_no_intrinsic.inc b/absl/numeric/int128_no_intrinsic.inc
index 195b745..a7cdcea 100644
--- a/absl/numeric/int128_no_intrinsic.inc
+++ b/absl/numeric/int128_no_intrinsic.inc
@@ -139,26 +139,29 @@
// complement overwhelms the precision of the mantissa.
//
// Also check to make sure we don't negate Int128Min()
+ constexpr float pow_2_64 = 18446744073709551616.0f;
return hi_ < 0 && *this != Int128Min()
? -static_cast<float>(-*this)
: static_cast<float>(lo_) +
- std::ldexp(static_cast<float>(hi_), 64);
+ static_cast<float>(hi_) * pow_2_64;
}
inline int128::operator double() const {
// See comment in int128::operator float() above.
+ constexpr double pow_2_64 = 18446744073709551616.0;
return hi_ < 0 && *this != Int128Min()
? -static_cast<double>(-*this)
: static_cast<double>(lo_) +
- std::ldexp(static_cast<double>(hi_), 64);
+ static_cast<double>(hi_) * pow_2_64;
}
inline int128::operator long double() const {
// See comment in int128::operator float() above.
+ constexpr long double pow_2_64 = 18446744073709551616.0L;
return hi_ < 0 && *this != Int128Min()
? -static_cast<long double>(-*this)
: static_cast<long double>(lo_) +
- std::ldexp(static_cast<long double>(hi_), 64);
+ static_cast<long double>(hi_) * pow_2_64;
}
// Comparison operators.