diff options
Diffstat (limited to 'absl/numeric/int128.cc')
-rw-r--r-- | absl/numeric/int128.cc | 30 |
1 files changed, 26 insertions, 4 deletions
diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc index 98af84b7..a22f1e3e 100644 --- a/absl/numeric/int128.cc +++ b/absl/numeric/int128.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include <type_traits> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { const uint128 kuint128max = MakeUint128(std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max()); @@ -66,7 +66,7 @@ static inline int Fls128(uint128 n) { // Long division/modulo for uint128 implemented using the shift-subtract // division algorithm adapted from: -// http://stackoverflow.com/questions/5386377/division-without-using +// https://stackoverflow.com/questions/5386377/division-without-using void DivModImpl(uint128 dividend, uint128 divisor, uint128* quotient_ret, uint128* remainder_ret) { assert(divisor != 0); @@ -124,6 +124,28 @@ uint128 MakeUint128FromFloat(T v) { return MakeUint128(0, static_cast<uint64_t>(v)); } + +#if defined(__clang__) && !defined(__SSE3__) +// Workaround for clang bug: https://bugs.llvm.org/show_bug.cgi?id=38289 +// Casting from long double to uint64_t is miscompiled and drops bits. +// It is more work, so only use when we need the workaround. +uint128 MakeUint128FromFloat(long double v) { + // Go 50 bits at a time, that fits in a double + static_assert(std::numeric_limits<double>::digits >= 50, ""); + static_assert(std::numeric_limits<long double>::digits <= 150, ""); + // Undefined behavior if v is not finite or cannot fit into uint128. + assert(std::isfinite(v) && v > -1 && v < std::ldexp(1.0L, 128)); + + v = std::ldexp(v, -100); + uint64_t w0 = static_cast<uint64_t>(static_cast<double>(std::trunc(v))); + v = std::ldexp(v - static_cast<double>(w0), 50); + uint64_t w1 = static_cast<uint64_t>(static_cast<double>(std::trunc(v))); + v = std::ldexp(v - static_cast<double>(w1), 50); + uint64_t w2 = static_cast<uint64_t>(static_cast<double>(std::trunc(v))); + return (static_cast<uint128>(w0) << 100) | (static_cast<uint128>(w1) << 50) | + static_cast<uint128>(w2); +} +#endif // __clang__ && !__SSE3__ } // namespace uint128::uint128(float v) : uint128(MakeUint128FromFloat(v)) {} @@ -223,7 +245,7 @@ std::ostream& operator<<(std::ostream& os, uint128 v) { return os << rep; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl namespace std { |