// Copyright 2020-2023 Daniel Lemire // Copyright 2023 Matt Borland // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // // If the architecture (e.g. Apple ARM) does not have __int128 we need to emulate it #ifndef BOOST_CHARCONV_DETAIL_EMULATED128_HPP #define BOOST_CHARCONV_DETAIL_EMULATED128_HPP #include #include #include #include #include #include #include #include namespace boost { namespace charconv { namespace detail { // Compilers might support built-in 128-bit integer types. However, it seems that // emulating them with a pair of 64-bit integers actually produces a better code, // so we avoid using those built-ins. That said, they are still useful for // implementing 64-bit x 64-bit -> 128-bit multiplication. // Memcpy-able temp class for uint128 struct trivial_uint128 { #if BOOST_CHARCONV_ENDIAN_LITTLE_BYTE std::uint64_t low; std::uint64_t high; #else std::uint64_t high; std::uint64_t low; #endif }; // Macro replacement lists can not be enclosed in parentheses struct uint128 { std::uint64_t high; std::uint64_t low; // Constructors constexpr uint128() noexcept : high {}, low {} {} constexpr uint128(const uint128& v) noexcept = default; constexpr uint128(uint128&& v) noexcept = default; constexpr uint128(std::uint64_t high_, std::uint64_t low_) noexcept : high {high_}, low {low_} {} constexpr uint128(const trivial_uint128& v) noexcept : high {v.high}, low {v.low} {} // NOLINT constexpr uint128(trivial_uint128&& v) noexcept : high {v.high}, low {v.low} {} // NOLINT #define SIGNED_CONSTRUCTOR(expr) constexpr uint128(expr v) noexcept : high {v < 0 ? UINT64_MAX : UINT64_C(0)}, low {static_cast(v)} {} // NOLINT #define UNSIGNED_CONSTRUCTOR(expr) constexpr uint128(expr v) noexcept : high {}, low {static_cast(v)} {} // NOLINT SIGNED_CONSTRUCTOR(char) // NOLINT SIGNED_CONSTRUCTOR(signed char) // NOLINT SIGNED_CONSTRUCTOR(short) // NOLINT SIGNED_CONSTRUCTOR(int) // NOLINT SIGNED_CONSTRUCTOR(long) // NOLINT SIGNED_CONSTRUCTOR(long long) // NOLINT UNSIGNED_CONSTRUCTOR(unsigned char) // NOLINT UNSIGNED_CONSTRUCTOR(unsigned short) // NOLINT UNSIGNED_CONSTRUCTOR(unsigned) // NOLINT UNSIGNED_CONSTRUCTOR(unsigned long) // NOLINT UNSIGNED_CONSTRUCTOR(unsigned long long) // NOLINT #ifdef BOOST_CHARCONV_HAS_INT128 constexpr uint128(boost::int128_type v) noexcept : // NOLINT : Allow implicit conversions high {static_cast(v >> 64)}, low {static_cast(static_cast(v) & ~UINT64_C(0))} {} constexpr uint128(boost::uint128_type v) noexcept : // NOLINT : Allow implicit conversions high {static_cast(v >> 64)}, low {static_cast(v & ~UINT64_C(0))} {} #endif #undef SIGNED_CONSTRUCTOR #undef UNSIGNED_CONSTRUCTOR // Assignment Operators #define SIGNED_ASSIGNMENT_OPERATOR(expr) BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator=(const expr& v) noexcept { high = v < 0 ? UINT64_MAX : UINT64_C(0); low = static_cast(v); return *this; } // NOLINT #define UNSIGNED_ASSIGNMENT_OPERATOR(expr) BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator=(const expr& v) noexcept { high = 0U; low = static_cast(v); return *this; } // NOLINT SIGNED_ASSIGNMENT_OPERATOR(char) // NOLINT SIGNED_ASSIGNMENT_OPERATOR(signed char) // NOLINT SIGNED_ASSIGNMENT_OPERATOR(short) // NOLINT SIGNED_ASSIGNMENT_OPERATOR(int) // NOLINT SIGNED_ASSIGNMENT_OPERATOR(long) // NOLINT SIGNED_ASSIGNMENT_OPERATOR(long long) // NOLINT UNSIGNED_ASSIGNMENT_OPERATOR(unsigned char) // NOLINT UNSIGNED_ASSIGNMENT_OPERATOR(unsigned short) // NOLINT UNSIGNED_ASSIGNMENT_OPERATOR(unsigned) // NOLINT UNSIGNED_ASSIGNMENT_OPERATOR(unsigned long) // NOLINT UNSIGNED_ASSIGNMENT_OPERATOR(unsigned long long) // NOLINT #ifdef BOOST_CHARCONV_HAS_INT128 BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator=(const boost::int128_type& v) noexcept { *this = uint128(v); return *this; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator=(const boost::uint128_type& v) noexcept { *this = uint128(v); return *this; } #endif BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator=(const trivial_uint128& v) noexcept { this->low = v.low; this->high = v.high; return *this; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator=(const uint128&) noexcept; #undef SIGNED_ASSIGNMENT_OPERATOR #undef UNSIGNED_ASSIGNMENT_OPERATOR // Conversion Operators #define INTEGER_CONVERSION_OPERATOR(expr) explicit constexpr operator expr() const noexcept { return static_cast(low); } // NOLINT #define FLOAT_CONVERSION_OPERATOR(expr) explicit operator expr() const noexcept { return std::ldexp(static_cast(high), 64) + static_cast(low); } // NOLINT INTEGER_CONVERSION_OPERATOR(char) // NOLINT INTEGER_CONVERSION_OPERATOR(signed char) // NOLINT INTEGER_CONVERSION_OPERATOR(short) // NOLINT INTEGER_CONVERSION_OPERATOR(int) // NOLINT INTEGER_CONVERSION_OPERATOR(long) // NOLINT INTEGER_CONVERSION_OPERATOR(long long) // NOLINT INTEGER_CONVERSION_OPERATOR(unsigned char) // NOLINT INTEGER_CONVERSION_OPERATOR(unsigned short) // NOLINT INTEGER_CONVERSION_OPERATOR(unsigned) // NOLINT INTEGER_CONVERSION_OPERATOR(unsigned long) // NOLINT INTEGER_CONVERSION_OPERATOR(unsigned long long) // NOLINT explicit constexpr operator bool() const noexcept { return high || low; } #ifdef BOOST_CHARCONV_HAS_INT128 explicit constexpr operator boost::int128_type() const noexcept { return (static_cast(high) << 64) + low; } explicit constexpr operator boost::uint128_type() const noexcept { return (static_cast(high) << 64) + low; } #endif FLOAT_CONVERSION_OPERATOR(float) // NOLINT FLOAT_CONVERSION_OPERATOR(double) // NOLINT FLOAT_CONVERSION_OPERATOR(long double) // NOLINT #undef INTEGER_CONVERSION_OPERATOR #undef FLOAT_CONVERSION_OPERATOR // Unary Operators constexpr friend uint128 operator-(uint128 val) noexcept; constexpr friend uint128 operator+(uint128 val) noexcept; // Comparison Operators // Equality #define INTEGER_OPERATOR_EQUAL(expr) constexpr friend bool operator==(uint128 lhs, expr rhs) noexcept { return lhs.high == 0 && rhs >= 0 && lhs.low == static_cast(rhs); } // NOLINT #define UNSIGNED_INTEGER_OPERATOR_EQUAL(expr) constexpr friend bool operator==(uint128 lhs, expr rhs) noexcept { return lhs.high == 0 && lhs.low == static_cast(rhs); } // NOLINT INTEGER_OPERATOR_EQUAL(char) // NOLINT INTEGER_OPERATOR_EQUAL(signed char) // NOLINT INTEGER_OPERATOR_EQUAL(short) // NOLINT INTEGER_OPERATOR_EQUAL(int) // NOLINT INTEGER_OPERATOR_EQUAL(long) // NOLINT INTEGER_OPERATOR_EQUAL(long long) // NOLINT UNSIGNED_INTEGER_OPERATOR_EQUAL(unsigned char) // NOLINT UNSIGNED_INTEGER_OPERATOR_EQUAL(unsigned short) // NOLINT UNSIGNED_INTEGER_OPERATOR_EQUAL(unsigned) // NOLINT UNSIGNED_INTEGER_OPERATOR_EQUAL(unsigned long) // NOLINT UNSIGNED_INTEGER_OPERATOR_EQUAL(unsigned long long) // NOLINT #ifdef BOOST_CHARCONV_HAS_INT128 constexpr friend bool operator==(uint128 lhs, boost::int128_type rhs) noexcept { return lhs == uint128(rhs); } constexpr friend bool operator==(uint128 lhs, boost::uint128_type rhs) noexcept { return lhs == uint128(rhs); } #endif constexpr friend bool operator==(uint128 lhs, uint128 rhs) noexcept; #undef INTEGER_OPERATOR_EQUAL #undef UNSIGNED_INTEGER_OPERATOR_EQUAL // Inequality #define INTEGER_OPERATOR_NOTEQUAL(expr) constexpr friend bool operator!=(uint128 lhs, expr rhs) noexcept { return !(lhs == rhs); } // NOLINT INTEGER_OPERATOR_NOTEQUAL(char) // NOLINT INTEGER_OPERATOR_NOTEQUAL(signed char) // NOLINT INTEGER_OPERATOR_NOTEQUAL(short) // NOLINT INTEGER_OPERATOR_NOTEQUAL(int) // NOLINT INTEGER_OPERATOR_NOTEQUAL(long) // NOLINT INTEGER_OPERATOR_NOTEQUAL(long long) // NOLINT INTEGER_OPERATOR_NOTEQUAL(unsigned char) // NOLINT INTEGER_OPERATOR_NOTEQUAL(unsigned short) // NOLINT INTEGER_OPERATOR_NOTEQUAL(unsigned) // NOLINT INTEGER_OPERATOR_NOTEQUAL(unsigned long) // NOLINT INTEGER_OPERATOR_NOTEQUAL(unsigned long long) // NOLINT #ifdef BOOST_CHARCONV_HAS_INT128 constexpr friend bool operator!=(uint128 lhs, boost::int128_type rhs) noexcept { return !(lhs == rhs); } constexpr friend bool operator!=(uint128 lhs, boost::uint128_type rhs) noexcept { return !(lhs == rhs); } #endif constexpr friend bool operator!=(uint128 lhs, uint128 rhs) noexcept; #undef INTEGER_OPERATOR_NOTEQUAL // Less than #define INTEGER_OPERATOR_LESS_THAN(expr) constexpr friend bool operator<(uint128 lhs, expr rhs) noexcept { return lhs.high == 0U && rhs > 0 && lhs.low < static_cast(rhs); } // NOLINT #define UNSIGNED_INTEGER_OPERATOR_LESS_THAN(expr) constexpr friend bool operator<(uint128 lhs, expr rhs) noexcept { return lhs.high == 0U && lhs.low < static_cast(rhs); } // NOLINT INTEGER_OPERATOR_LESS_THAN(char) // NOLINT INTEGER_OPERATOR_LESS_THAN(signed char) // NOLINT INTEGER_OPERATOR_LESS_THAN(short) // NOLINT INTEGER_OPERATOR_LESS_THAN(int) // NOLINT INTEGER_OPERATOR_LESS_THAN(long) // NOLINT INTEGER_OPERATOR_LESS_THAN(long long) // NOLINT UNSIGNED_INTEGER_OPERATOR_LESS_THAN(unsigned char) // NOLINT UNSIGNED_INTEGER_OPERATOR_LESS_THAN(unsigned short) // NOLINT UNSIGNED_INTEGER_OPERATOR_LESS_THAN(unsigned) // NOLINT UNSIGNED_INTEGER_OPERATOR_LESS_THAN(unsigned long) // NOLINT UNSIGNED_INTEGER_OPERATOR_LESS_THAN(unsigned long long) // NOLINT #ifdef BOOST_CHARCONV_HAS_INT128 BOOST_CHARCONV_CXX14_CONSTEXPR friend bool operator<(uint128 lhs, boost::int128_type rhs) noexcept { return lhs < uint128(rhs); } BOOST_CHARCONV_CXX14_CONSTEXPR friend bool operator<(uint128 lhs, boost::uint128_type rhs) noexcept { return lhs < uint128(rhs); } #endif BOOST_CHARCONV_CXX14_CONSTEXPR friend bool operator<(uint128 lhs, uint128 rhs) noexcept; #undef INTEGER_OPERATOR_LESS_THAN #undef UNSIGNED_INTEGER_OPERATOR_LESS_THAN // Less than or equal to #define INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(expr) constexpr friend bool operator<=(uint128 lhs, expr rhs) noexcept { return lhs.high == 0U && rhs >= 0 && lhs.low <= static_cast(rhs); } // NOLINT #define UNSIGNED_INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(expr) constexpr friend bool operator<=(uint128 lhs, expr rhs) noexcept { return lhs.high == 0U && lhs.low <= static_cast(rhs); } // NOLINT INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(char) // NOLINT INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(signed char) // NOLINT INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(short) // NOLINT INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(int) // NOLINT INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(long) // NOLINT INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(long long) // NOLINT UNSIGNED_INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(unsigned char) // NOLINT UNSIGNED_INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(unsigned short) // NOLINT UNSIGNED_INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(unsigned) // NOLINT UNSIGNED_INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(unsigned long) // NOLINT UNSIGNED_INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO(unsigned long long) // NOLINT #ifdef BOOST_CHARCONV_HAS_INT128 BOOST_CHARCONV_CXX14_CONSTEXPR friend bool operator<=(uint128 lhs, boost::int128_type rhs) noexcept { return lhs <= uint128(rhs); } BOOST_CHARCONV_CXX14_CONSTEXPR friend bool operator<=(uint128 lhs, boost::uint128_type rhs) noexcept { return lhs <= uint128(rhs); } #endif BOOST_CHARCONV_CXX14_CONSTEXPR friend bool operator<=(uint128 lhs, uint128 rhs) noexcept; #undef INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO #undef UNSIGNED_INTEGER_OPERATOR_LESS_THAN_OR_EQUAL_TO // Greater than #define INTEGER_OPERATOR_GREATER_THAN(expr) constexpr friend bool operator>(uint128 lhs, expr rhs) noexcept { return lhs.high > 0U || rhs < 0 || lhs.low > static_cast(rhs); } // NOLINT #define UNSIGNED_INTEGER_OPERATOR_GREATER_THAN(expr) constexpr friend bool operator>(uint128 lhs, expr rhs) noexcept { return lhs.high > 0U || lhs.low > static_cast(rhs); } // NOLINT INTEGER_OPERATOR_GREATER_THAN(char) // NOLINT INTEGER_OPERATOR_GREATER_THAN(signed char) // NOLINT INTEGER_OPERATOR_GREATER_THAN(short) // NOLINT INTEGER_OPERATOR_GREATER_THAN(int) // NOLINT INTEGER_OPERATOR_GREATER_THAN(long) // NOLINT INTEGER_OPERATOR_GREATER_THAN(long long) // NOLINT UNSIGNED_INTEGER_OPERATOR_GREATER_THAN(unsigned char) // NOLINT UNSIGNED_INTEGER_OPERATOR_GREATER_THAN(unsigned short) // NOLINT UNSIGNED_INTEGER_OPERATOR_GREATER_THAN(unsigned) // NOLINT UNSIGNED_INTEGER_OPERATOR_GREATER_THAN(unsigned long) // NOLINT UNSIGNED_INTEGER_OPERATOR_GREATER_THAN(unsigned long long) // NOLINT #ifdef BOOST_CHARCONV_HAS_INT128 BOOST_CHARCONV_CXX14_CONSTEXPR friend bool operator>(uint128 lhs, boost::int128_type rhs) noexcept { return lhs > uint128(rhs); } BOOST_CHARCONV_CXX14_CONSTEXPR friend bool operator>(uint128 lhs, boost::uint128_type rhs) noexcept { return lhs > uint128(rhs); } #endif BOOST_CHARCONV_CXX14_CONSTEXPR friend bool operator>(uint128 lhs, uint128 rhs) noexcept; #undef INTEGER_OPERATOR_GREATER_THAN #undef UNSIGNED_INTEGER_OPERATOR_GREATER_THAN // Greater than or equal to #define INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(expr) constexpr friend bool operator>=(uint128 lhs, expr rhs) noexcept { return lhs.high > 0U || rhs < 0 || lhs.low >= static_cast(rhs); } // NOLINT #define UNSIGNED_INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(expr) constexpr friend bool operator>=(uint128 lhs, expr rhs) noexcept { return lhs.high > 0U || lhs.low >= static_cast(rhs); } // NOLINT INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(char) // NOLINT INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(signed char) // NOLINT INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(short) // NOLINT INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(int) // NOLINT INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(long) // NOLINT INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(long long) // NOLINT UNSIGNED_INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(unsigned char) // NOLINT UNSIGNED_INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(unsigned short) // NOLINT UNSIGNED_INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(unsigned) // NOLINT UNSIGNED_INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(unsigned long) // NOLINT UNSIGNED_INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO(unsigned long long) // NOLINT #ifdef BOOST_CHARCONV_HAS_INT128 BOOST_CHARCONV_CXX14_CONSTEXPR friend bool operator>=(uint128 lhs, boost::int128_type rhs) noexcept { return lhs >= uint128(rhs); } BOOST_CHARCONV_CXX14_CONSTEXPR friend bool operator>=(uint128 lhs, boost::uint128_type rhs) noexcept { return lhs >= uint128(rhs); } #endif BOOST_CHARCONV_CXX14_CONSTEXPR friend bool operator>=(uint128 lhs, uint128 rhs) noexcept; #undef INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO #undef UNSIGNED_INTEGER_OPERATOR_GREATER_THAN_OR_EQUAL_TO // Binary Operators // Not constexpr friend uint128 operator~(uint128 v) noexcept; // Or #define INTEGER_BINARY_OPERATOR_OR(expr) constexpr friend uint128 operator|(uint128 lhs, expr rhs) noexcept { return {lhs.high, lhs.low | static_cast(rhs)}; } // NOLINT INTEGER_BINARY_OPERATOR_OR(char) // NOLINT INTEGER_BINARY_OPERATOR_OR(signed char) // NOLINT INTEGER_BINARY_OPERATOR_OR(short) // NOLINT INTEGER_BINARY_OPERATOR_OR(int) // NOLINT INTEGER_BINARY_OPERATOR_OR(long) // NOLINT INTEGER_BINARY_OPERATOR_OR(long long) // NOLINT INTEGER_BINARY_OPERATOR_OR(unsigned char) // NOLINT INTEGER_BINARY_OPERATOR_OR(unsigned short) // NOLINT INTEGER_BINARY_OPERATOR_OR(unsigned) // NOLINT INTEGER_BINARY_OPERATOR_OR(unsigned long) // NOLINT INTEGER_BINARY_OPERATOR_OR(unsigned long long) // NOLINT #ifdef BOOST_CHARCONV_HAS_INT128 constexpr friend uint128 operator|(uint128 lhs, boost::int128_type rhs) noexcept { return lhs | uint128(rhs); } constexpr friend uint128 operator|(uint128 lhs, boost::uint128_type rhs) noexcept { return lhs | uint128(rhs); } #endif constexpr friend uint128 operator|(uint128 lhs, uint128 rhs) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator|=(uint128 v) noexcept; #undef INTEGER_BINARY_OPERATOR_OR // And #define INTEGER_BINARY_OPERATOR_AND(expr) constexpr friend uint128 operator&(uint128 lhs, expr rhs) noexcept { return {lhs.high, lhs.low & static_cast(rhs)}; } // NOLINT INTEGER_BINARY_OPERATOR_AND(char) // NOLINT INTEGER_BINARY_OPERATOR_AND(signed char) // NOLINT INTEGER_BINARY_OPERATOR_AND(short) // NOLINT INTEGER_BINARY_OPERATOR_AND(int) // NOLINT INTEGER_BINARY_OPERATOR_AND(long) // NOLINT INTEGER_BINARY_OPERATOR_AND(long long) // NOLINT INTEGER_BINARY_OPERATOR_AND(unsigned char) // NOLINT INTEGER_BINARY_OPERATOR_AND(unsigned short) // NOLINT INTEGER_BINARY_OPERATOR_AND(unsigned) // NOLINT INTEGER_BINARY_OPERATOR_AND(unsigned long) // NOLINT INTEGER_BINARY_OPERATOR_AND(unsigned long long) // NOLINT #ifdef BOOST_CHARCONV_HAS_INT128 constexpr friend uint128 operator&(uint128 lhs, boost::int128_type rhs) noexcept { return lhs & uint128(rhs); } constexpr friend uint128 operator&(uint128 lhs, boost::uint128_type rhs) noexcept { return lhs & uint128(rhs); } #endif constexpr friend uint128 operator&(uint128 lhs, uint128 rhs) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator&=(uint128 v) noexcept; #undef INTEGER_BINARY_OPERATOR_AND // Xor #define INTEGER_BINARY_OPERATOR_XOR(expr) constexpr friend uint128 operator^(uint128 lhs, expr rhs) noexcept { return {lhs.high, lhs.low ^ static_cast(rhs)}; } // NOLINT INTEGER_BINARY_OPERATOR_XOR(char) // NOLINT INTEGER_BINARY_OPERATOR_XOR(signed char) // NOLINT INTEGER_BINARY_OPERATOR_XOR(short) // NOLINT INTEGER_BINARY_OPERATOR_XOR(int) // NOLINT INTEGER_BINARY_OPERATOR_XOR(long) // NOLINT INTEGER_BINARY_OPERATOR_XOR(long long) // NOLINT INTEGER_BINARY_OPERATOR_XOR(unsigned char) // NOLINT INTEGER_BINARY_OPERATOR_XOR(unsigned short) // NOLINT INTEGER_BINARY_OPERATOR_XOR(unsigned) // NOLINT INTEGER_BINARY_OPERATOR_XOR(unsigned long) // NOLINT INTEGER_BINARY_OPERATOR_XOR(unsigned long long) // NOLINT #ifdef BOOST_CHARCONV_HAS_INT128 constexpr friend uint128 operator^(uint128 lhs, boost::int128_type rhs) noexcept { return lhs ^ uint128(rhs); } constexpr friend uint128 operator^(uint128 lhs, boost::uint128_type rhs) noexcept { return lhs ^ uint128(rhs); } #endif constexpr friend uint128 operator^(uint128 lhs, uint128 rhs) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator^=(uint128 v) noexcept; #undef INTEGER_BINARY_OPERATOR_XOR // Left shift #define INTEGER_BINARY_OPERATOR_LEFT_SHIFT(expr) \ BOOST_CHARCONV_CXX14_CONSTEXPR friend uint128 operator<<(uint128 lhs, expr rhs) noexcept \ { \ if (rhs >= 64) \ { \ return {lhs.low << (rhs - 64), 0}; \ } \ else if (rhs == 0) \ { \ return lhs; \ } \ \ return {(lhs.high << rhs) | (lhs.low >> (64 - rhs)), lhs.low << rhs}; \ } // NOLINT INTEGER_BINARY_OPERATOR_LEFT_SHIFT(char) // NOLINT INTEGER_BINARY_OPERATOR_LEFT_SHIFT(signed char) // NOLINT INTEGER_BINARY_OPERATOR_LEFT_SHIFT(short) // NOLINT INTEGER_BINARY_OPERATOR_LEFT_SHIFT(int) // NOLINT INTEGER_BINARY_OPERATOR_LEFT_SHIFT(long) // NOLINT INTEGER_BINARY_OPERATOR_LEFT_SHIFT(long long) // NOLINT INTEGER_BINARY_OPERATOR_LEFT_SHIFT(unsigned char) // NOLINT INTEGER_BINARY_OPERATOR_LEFT_SHIFT(unsigned short) // NOLINT INTEGER_BINARY_OPERATOR_LEFT_SHIFT(unsigned) // NOLINT INTEGER_BINARY_OPERATOR_LEFT_SHIFT(unsigned long) // NOLINT INTEGER_BINARY_OPERATOR_LEFT_SHIFT(unsigned long long) // NOLINT #define INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT(expr) \ BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator<<=(expr amount) noexcept \ { \ *this = *this << amount; \ return *this; \ } // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT(char) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT(signed char) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT(short) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT(int) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT(long) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT(long long) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT(unsigned char) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT(unsigned short) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT(unsigned) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT(unsigned long) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT(unsigned long long) // NOLINT #undef INTEGER_BINARY_OPERATOR_LEFT_SHIFT #undef INTEGER_BINARY_OPERATOR_EQUALS_LEFT_SHIFT // Right Shift #define INTEGER_BINARY_OPERATOR_RIGHT_SHIFT(expr) \ BOOST_CHARCONV_CXX14_CONSTEXPR friend uint128 operator>>(uint128 lhs, expr amount) noexcept \ { \ if (amount >= 64) \ { \ return {0, lhs.high >> (amount - 64)}; \ } \ else if (amount == 0) \ { \ return lhs; \ } \ \ return {lhs.high >> amount, (lhs.low >> amount) | (lhs.high << (64 - amount))}; \ } // NOLINT INTEGER_BINARY_OPERATOR_RIGHT_SHIFT(char) // NOLINT INTEGER_BINARY_OPERATOR_RIGHT_SHIFT(signed char) // NOLINT INTEGER_BINARY_OPERATOR_RIGHT_SHIFT(short) // NOLINT INTEGER_BINARY_OPERATOR_RIGHT_SHIFT(int) // NOLINT INTEGER_BINARY_OPERATOR_RIGHT_SHIFT(long) // NOLINT INTEGER_BINARY_OPERATOR_RIGHT_SHIFT(long long) // NOLINT INTEGER_BINARY_OPERATOR_RIGHT_SHIFT(unsigned char) // NOLINT INTEGER_BINARY_OPERATOR_RIGHT_SHIFT(unsigned short) // NOLINT INTEGER_BINARY_OPERATOR_RIGHT_SHIFT(unsigned) // NOLINT INTEGER_BINARY_OPERATOR_RIGHT_SHIFT(unsigned long) // NOLINT INTEGER_BINARY_OPERATOR_RIGHT_SHIFT(unsigned long long) // NOLINT #define INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT(expr) \ BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator>>=(expr amount) noexcept \ { \ *this = *this >> amount; \ return *this; \ } // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT(char) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT(signed char) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT(short) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT(int) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT(long) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT(long long) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT(unsigned char) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT(unsigned short) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT(unsigned) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT(unsigned long) // NOLINT INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT(unsigned long long) // NOLINT #undef INTEGER_BINARY_OPERATOR_RIGHT_SHIFT #undef INTEGER_BINARY_OPERATOR_EQUALS_RIGHT_SHIFT // Arithmetic operators (Add, sub, mul, div, mod) inline uint128 &operator+=(std::uint64_t n) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR friend uint128 operator+(uint128 lhs, uint128 rhs) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator+=(uint128 v) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator++() noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR const uint128 operator++(int) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR friend uint128 operator-(uint128 lhs, uint128 rhs) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator-=(uint128 v) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator--() noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR const uint128 operator--(int) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR friend uint128 operator*(uint128 lhs, uint128 rhs) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator*=(uint128 v) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR friend uint128 operator/(uint128 lhs, uint128 rhs) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator/=(uint128 v) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR friend uint128 operator%(uint128 lhs, uint128 rhs) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &operator%=(uint128 v) noexcept; private: BOOST_CHARCONV_CXX14_CONSTEXPR friend int high_bit(uint128 v) noexcept; BOOST_CHARCONV_CXX14_CONSTEXPR friend void div_impl(uint128 lhs, uint128 rhs, uint128 "ient, uint128 &remainder) noexcept; }; constexpr uint128 operator-(uint128 val) noexcept { return {~val.high + static_cast(val.low == 0), ~val.low + 1}; } constexpr uint128 operator+(uint128 val) noexcept { return val; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &uint128::operator=(const uint128& v) noexcept // NOLINT : User defined for older compilers { low = v.low; high = v.high; return *this; } constexpr bool operator==(uint128 lhs, uint128 rhs) noexcept { return lhs.high == rhs.high && lhs.low == rhs.low; } constexpr bool operator!=(uint128 lhs, uint128 rhs) noexcept { return !(lhs == rhs); } BOOST_CHARCONV_CXX14_CONSTEXPR bool operator<(uint128 lhs, uint128 rhs) noexcept { if (lhs.high == rhs.high) { return lhs.low < rhs.low; } return lhs.high < rhs.high; } BOOST_CHARCONV_CXX14_CONSTEXPR bool operator<=(uint128 lhs, uint128 rhs) noexcept { return !(rhs < lhs); } BOOST_CHARCONV_CXX14_CONSTEXPR bool operator>(uint128 lhs, uint128 rhs) noexcept { return rhs < lhs; } BOOST_CHARCONV_CXX14_CONSTEXPR bool operator>=(uint128 lhs, uint128 rhs) noexcept { return !(lhs < rhs); } constexpr uint128 operator~(uint128 v) noexcept { return {~v.high, ~v.low}; } constexpr uint128 operator|(uint128 lhs, uint128 rhs) noexcept { return {lhs.high | rhs.high, lhs.low | rhs.low}; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &uint128::operator|=(uint128 v) noexcept { *this = *this | v; return *this; } constexpr uint128 operator&(uint128 lhs, uint128 rhs) noexcept { return {lhs.high & rhs.high, lhs.low & rhs.low}; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &uint128::operator&=(uint128 v) noexcept { *this = *this & v; return *this; } constexpr uint128 operator^(uint128 lhs, uint128 rhs) noexcept { return {lhs.high ^ rhs.high, lhs.low ^ rhs.low}; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &uint128::operator^=(uint128 v) noexcept { *this = *this ^ v; return *this; } inline uint128 &uint128::operator+=(std::uint64_t n) noexcept { #if BOOST_CHARCONV_HAS_BUILTIN(__builtin_addcll) unsigned long long carry {}; low = __builtin_addcll(low, n, 0, &carry); high = __builtin_addcll(high, 0, carry, &carry); #elif BOOST_CHARCONV_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) unsigned long long result {}; auto carry = __builtin_ia32_addcarryx_u64(0, low, n, &result); low = result; __builtin_ia32_addcarryx_u64(carry, high, 0, &result); high = result; #elif defined(BOOST_MSVC) && defined(_M_X64) auto carry = _addcarry_u64(0, low, n, &low); _addcarry_u64(carry, high, 0, &high); #else auto sum = low + n; high += (sum < low ? 1 : 0); low = sum; #endif return *this; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 operator+(uint128 lhs, uint128 rhs) noexcept { const uint128 temp = {lhs.high + rhs.high, lhs.low + rhs.low}; // Need to carry a bit into rhs if (temp.low < lhs.low) { return {temp.high + 1, temp.low}; } return temp; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &uint128::operator+=(uint128 v) noexcept { *this = *this + v; return *this; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &uint128::operator++() noexcept { if (this->low == UINT64_MAX) { this->low = 0; ++this->high; } else { ++this->low; } return *this; } BOOST_CHARCONV_CXX14_CONSTEXPR const uint128 uint128::operator++(int) noexcept { return ++(*this); } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 operator-(uint128 lhs, uint128 rhs) noexcept { const uint128 temp {lhs.high - rhs.high, lhs.low - rhs.low}; // Check for carry if (lhs.low < rhs.low) { return {temp.high - 1, temp.low}; } return temp; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &uint128::operator-=(uint128 v) noexcept { *this = *this - v; return *this; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &uint128::operator--() noexcept { if (this->low == 0) { this->low = UINT64_MAX; --this->high; } else // NOLINT { --this->low; } return *this; } BOOST_CHARCONV_CXX14_CONSTEXPR const uint128 uint128::operator--(int) noexcept { return --(*this); } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 operator*(uint128 lhs, uint128 rhs) noexcept { const auto a = static_cast(lhs.low >> 32); const auto b = static_cast(lhs.low & UINT32_MAX); const auto c = static_cast(rhs.low >> 32); const auto d = static_cast(rhs.low & UINT32_MAX); uint128 result { lhs.high * rhs.low + lhs.low * rhs.high + a * c, b * d }; result += uint128(a * d) << 32; result += uint128(b * c) << 32; return result; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &uint128::operator*=(uint128 v) noexcept { *this = *this * v; return *this; } BOOST_CHARCONV_CXX14_CONSTEXPR int high_bit(uint128 v) noexcept { if (v.high != 0) { return 127 - boost::core::countl_zero(v.high); } else if (v.low != 0) { return 63 - boost::core::countl_zero(v.low); } return 0; } // See: https://stackoverflow.com/questions/5386377/division-without-using BOOST_CHARCONV_CXX14_CONSTEXPR void div_impl(uint128 lhs, uint128 rhs, uint128& quotient, uint128& remainder) noexcept { constexpr uint128 one {0, 1}; if (rhs > lhs) { quotient = 0U; remainder = 0U; } else if (lhs == rhs) { quotient = 1U; remainder = 0U; } uint128 denom = rhs; quotient = 0U; std::int32_t shift = high_bit(lhs) - high_bit(rhs); if (shift < 0) { shift = 32 - shift; } denom <<= shift; for (int i = 0; i <= shift; ++i) { quotient <<= 1; if (lhs >= denom) { lhs -= denom; quotient |= one; } denom >>= 1; } remainder = lhs; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 operator/(uint128 lhs, uint128 rhs) noexcept { uint128 quotient {0, 0}; uint128 remainder {0, 0}; div_impl(lhs, rhs, quotient, remainder); return quotient; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &uint128::operator/=(uint128 v) noexcept { *this = *this / v; return *this; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 operator%(uint128 lhs, uint128 rhs) noexcept { uint128 quotient {0, 0}; uint128 remainder {0, 0}; div_impl(lhs, rhs, quotient, remainder); return remainder; } BOOST_CHARCONV_CXX14_CONSTEXPR uint128 &uint128::operator%=(uint128 v) noexcept { *this = *this % v; return *this; } static inline std::uint64_t umul64(std::uint32_t x, std::uint32_t y) noexcept { // __emulu is not available on ARM https://learn.microsoft.com/en-us/cpp/intrinsics/emul-emulu?view=msvc-170 #if defined(BOOST_CHARCONV_HAS_MSVC_32BIT_INTRINSICS) && !defined(_M_ARM) return __emulu(x, y); #else return x * static_cast(y); #endif } // Get 128-bit result of multiplication of two 64-bit unsigned integers. BOOST_CHARCONV_SAFEBUFFERS inline uint128 umul128(std::uint64_t x, std::uint64_t y) noexcept { #if defined(BOOST_CHARCONV_HAS_INT128) auto result = static_cast(x) * static_cast(y); return {static_cast(result >> 64), static_cast(result)}; // _umul128 is x64 only https://learn.microsoft.com/en-us/cpp/intrinsics/umul128?view=msvc-170 #elif defined(BOOST_CHARCONV_HAS_MSVC_64BIT_INTRINSICS) && !defined(_M_ARM64) unsigned long long high; std::uint64_t low = _umul128(x, y, &high); return {static_cast(high), low}; // https://developer.arm.com/documentation/dui0802/a/A64-General-Instructions/UMULH #elif defined(_M_ARM64) && !defined(__MINGW32__) std::uint64_t high = __umulh(x, y); std::uint64_t low = x * y; return {high, low}; #else auto a = static_cast(x >> 32); auto b = static_cast(x); auto c = static_cast(y >> 32); auto d = static_cast(y); auto ac = umul64(a, c); auto bc = umul64(b, c); auto ad = umul64(a, d); auto bd = umul64(b, d); auto intermediate = (bd >> 32) + static_cast(ad) + static_cast(bc); return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), (intermediate << 32) + static_cast(bd)}; #endif } BOOST_CHARCONV_SAFEBUFFERS inline std::uint64_t umul128_upper64(std::uint64_t x, std::uint64_t y) noexcept { #if defined(BOOST_CHARCONV_HAS_INT128) auto result = static_cast(x) * static_cast(y); return static_cast(result >> 64); #elif defined(BOOST_CHARCONV_HAS_MSVC_64BIT_INTRINSICS) return __umulh(x, y); #else auto a = static_cast(x >> 32); auto b = static_cast(x); auto c = static_cast(y >> 32); auto d = static_cast(y); auto ac = umul64(a, c); auto bc = umul64(b, c); auto ad = umul64(a, d); auto bd = umul64(b, d); auto intermediate = (bd >> 32) + static_cast(ad) + static_cast(bc); return ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32); #endif } // Get upper 128-bits of multiplication of a 64-bit unsigned integer and a 128-bit // unsigned integer. BOOST_CHARCONV_SAFEBUFFERS inline uint128 umul192_upper128(std::uint64_t x, uint128 y) noexcept { auto r = umul128(x, y.high); r += umul128_upper64(x, y.low); return r; } // Get upper 64-bits of multiplication of a 32-bit unsigned integer and a 64-bit // unsigned integer. inline std::uint64_t umul96_upper64(std::uint32_t x, std::uint64_t y) noexcept { #if defined(BOOST_CHARCONV_HAS_INT128) || defined(BOOST_CHARCONV_HAS_MSVC_64BIT_INTRINSICS) return umul128_upper64(static_cast(x) << 32, y); #else auto yh = static_cast(y >> 32); auto yl = static_cast(y); auto xyh = umul64(x, yh); auto xyl = umul64(x, yl); return xyh + (xyl >> 32); #endif } // Get lower 128-bits of multiplication of a 64-bit unsigned integer and a 128-bit // unsigned integer. BOOST_CHARCONV_SAFEBUFFERS inline uint128 umul192_lower128(std::uint64_t x, uint128 y) noexcept { auto high = x * y.high; auto highlow = umul128(x, y.low); return {high + highlow.high, highlow.low}; } // Get lower 64-bits of multiplication of a 32-bit unsigned integer and a 64-bit // unsigned integer. inline std::uint64_t umul96_lower64(std::uint32_t x, std::uint64_t y) noexcept { return x * y; } }}} // Namespaces // Non-standard libraries may add specializations for library-provided types namespace std { template <> struct numeric_limits { // Member constants BOOST_ATTRIBUTE_UNUSED static constexpr bool is_specialized = true; BOOST_ATTRIBUTE_UNUSED static constexpr bool is_signed = false; BOOST_ATTRIBUTE_UNUSED static constexpr bool is_integer = true; BOOST_ATTRIBUTE_UNUSED static constexpr bool is_exact = true; BOOST_ATTRIBUTE_UNUSED static constexpr bool has_infinity = false; BOOST_ATTRIBUTE_UNUSED static constexpr bool has_quiet_NaN = false; BOOST_ATTRIBUTE_UNUSED static constexpr bool has_signaling_NaN = false; BOOST_ATTRIBUTE_UNUSED static constexpr std::float_round_style round_style = std::round_toward_zero; BOOST_ATTRIBUTE_UNUSED static constexpr bool is_iec559 = false; BOOST_ATTRIBUTE_UNUSED static constexpr bool is_bounded = true; BOOST_ATTRIBUTE_UNUSED static constexpr bool is_modulo = true; BOOST_ATTRIBUTE_UNUSED static constexpr int digits = 128; BOOST_ATTRIBUTE_UNUSED static constexpr int digits10 = 38; BOOST_ATTRIBUTE_UNUSED static constexpr int max_digits10 = 0; BOOST_ATTRIBUTE_UNUSED static constexpr int radix = 2; BOOST_ATTRIBUTE_UNUSED static constexpr int min_exponent = 0; BOOST_ATTRIBUTE_UNUSED static constexpr int min_exponent10 = 0; BOOST_ATTRIBUTE_UNUSED static constexpr int max_exponent = 0; BOOST_ATTRIBUTE_UNUSED static constexpr int max_exponent10 = 0; BOOST_ATTRIBUTE_UNUSED static constexpr bool traps = std::numeric_limits::traps; BOOST_ATTRIBUTE_UNUSED static constexpr bool tinyness_before = false; // Member functions BOOST_ATTRIBUTE_UNUSED static constexpr boost::charconv::detail::uint128 (min)() { return 0; } BOOST_ATTRIBUTE_UNUSED static constexpr boost::charconv::detail::uint128 lowest() { return 0; } BOOST_ATTRIBUTE_UNUSED static constexpr boost::charconv::detail::uint128 (max)() { return {UINT64_MAX, UINT64_MAX}; } BOOST_ATTRIBUTE_UNUSED static constexpr boost::charconv::detail::uint128 epsilon() { return 0; } BOOST_ATTRIBUTE_UNUSED static constexpr boost::charconv::detail::uint128 round_error() { return 0; } BOOST_ATTRIBUTE_UNUSED static constexpr boost::charconv::detail::uint128 infinity() { return 0; } BOOST_ATTRIBUTE_UNUSED static constexpr boost::charconv::detail::uint128 quiet_NaN() { return 0; } BOOST_ATTRIBUTE_UNUSED static constexpr boost::charconv::detail::uint128 signaling_NaN() { return 0; } BOOST_ATTRIBUTE_UNUSED static constexpr boost::charconv::detail::uint128 denorm_min() { return 0; } }; } // Namespace std #endif // BOOST_CHARCONV_DETAIL_EMULATED128_HPP