Per feed back for this question, I have written an unsigned 128 bit integer for Windows. The class first determines if it is in a 64 bit environment. It only creates the class if it is in a Windows 64 bit environment that supports MS intrinsic functions. Else it falls back to 32 bit mode. It also checks if either GCC or Clang unsigned 128 bit numbers are supported.
My biggest concern is of I got the macro definitions correct. I have not really done much in that area before.
uint_128_t.h
#include <compare>
#include <limits>
#include <string>
#if SIZE_MAX > 0xFFFFFFFFUL
#define ENV_64
#else
#define ENV_32
#endif
#if defined(__SIZEOF_INT128__)
#if (defined(__clang__) && !defined(_WIN32)) || (defined(__GNUC__) && !defined(__clang__)
#define INT_128
#endif
#elif defined(ENV_64)
#if defined(_MSC_VER) && !defined(_M_ARM64EC)
#define WIN_128
#include <intrin.h>
#include <immintrin.h>
#pragma intrinsic(_umul128, _udiv128)
#else
#define ENV_32 // Fallback to a 32 bit environment.
#endif
#endif
namespace Olly {
#if defined(WIN_128)
/********************************************************************************************/
//
// 'unsigned_int_128_t' class
//
// The unsigned_int_128_t class implements a Microsoft intrinsic unsigned 128 bit
// integer.
//
/********************************************************************************************/
class uint_128_t {
public:
uint_128_t();
uint_128_t(unsigned __int64 n);
~uint_128_t();
uint_128_t(uint_128_t&& obj) = default;
uint_128_t(const uint_128_t& obj) = default;
uint_128_t& operator=(uint_128_t&& obj) = default;
uint_128_t& operator=(const uint_128_t& obj) = default;
friend void swap(uint_128_t& first, uint_128_t& second);
operator unsigned __int64() const;
operator bool() const;
bool operator==(const uint_128_t& b) const;
bool operator!=(const uint_128_t& b) const;
std::partial_ordering operator<=>(const uint_128_t& b) const;
uint_128_t& operator&=(const uint_128_t& other);
uint_128_t& operator|=(const uint_128_t& other);
uint_128_t& operator^=(const uint_128_t& other);
uint_128_t operator&(const uint_128_t& b) const;
uint_128_t operator|(const uint_128_t& b) const;
uint_128_t operator^(const uint_128_t& b) const;
uint_128_t operator~() const;
friend uint_128_t operator&(uint_128_t&& a, const uint_128_t& b);
friend uint_128_t operator|(uint_128_t&& a, const uint_128_t& b);
friend uint_128_t operator^(uint_128_t&& a, const uint_128_t& b);
uint_128_t& operator<<=(unsigned __int64 index);
uint_128_t& operator>>=(unsigned __int64 index);
uint_128_t operator<<(unsigned __int64 index) const;
uint_128_t operator>>(unsigned __int64 index) const;
friend uint_128_t operator<<(uint_128_t&& a, unsigned __int64 index);
friend uint_128_t operator>>(uint_128_t&& a, unsigned __int64 index);
uint_128_t& operator+=(const uint_128_t& other);
uint_128_t& operator-=(const uint_128_t& other);
uint_128_t& operator*=(const uint_128_t& other);
uint_128_t& operator/=(const uint_128_t& other);
uint_128_t& operator%=(const uint_128_t& other);
uint_128_t operator+(const uint_128_t& b) const;
uint_128_t operator-(const uint_128_t& b) const;
uint_128_t operator*(const uint_128_t& b) const;
uint_128_t operator/(const uint_128_t& b) const;
uint_128_t operator%(const uint_128_t& b) const;
friend uint_128_t&& operator+(uint_128_t&& a, const uint_128_t& b);
friend uint_128_t&& operator-(uint_128_t&& a, const uint_128_t& b);
friend uint_128_t&& operator*(uint_128_t&& a, const uint_128_t& b);
friend uint_128_t&& operator/(uint_128_t&& a, const uint_128_t& b);
friend uint_128_t&& operator%(uint_128_t&& a, const uint_128_t& b);
uint_128_t& operator++();
uint_128_t operator++(int);
uint_128_t& operator--();
uint_128_t operator--(int);
unsigned __int64 upper() const;
unsigned __int64 lower() const;
private:
unsigned __int64 _upper;
unsigned __int64 _lower;
static const unsigned __int64 half_word_size = 32;
static const unsigned __int64 half_word_mask = (~unsigned __int64(0) >> half_word_size);
};
#endif
#if defined(ENV_64)
using word_t = uint_fast64_t;
#if defined(__GNUC__) && defined(INT_128)
using double_word_t = unsigned __int128;
#elif defined(__clang__) && defined(INT_128)
using double_word_t = unsigned __int128_t;
#elif defined(__xlC__) && defined(INT_128)
using double_word_t = __uint128_t;
#elif defined(WIN_128)
using double_word_t = uint_128_t;
#endif
#elif defined(ENV_32)
using word_t = uint_fast32_t;
using double_word_t = uint_fast64_t;
#endif
}
uint_128_t.cpp
#include "./uint_128_t.h"
namespace Olly {
#if defined(WIN_128)
uint_128_t::uint_128_t() : _upper(0), _lower(0) {
}
uint_128_t::uint_128_t(unsigned __int64 n) : _upper(0), _lower(n) {
}
uint_128_t::~uint_128_t() {
}
void swap(uint_128_t& left, uint_128_t& right) {
std::swap(left, right);
}
uint_128_t::operator unsigned __int64() const {
return _lower;
}
uint_128_t::operator bool() const {
return _upper || _lower;
}
bool uint_128_t::operator==(const uint_128_t& b) const {
return _upper == _upper && _lower == _lower;
}
bool uint_128_t::operator!=(const uint_128_t& b) const {
return !operator==(b);
}
std::partial_ordering uint_128_t::operator<=>(const uint_128_t& b) const {
if (_upper > b._upper) {
return std::partial_ordering::greater;
}
if (_upper < b._upper) {
return std::partial_ordering::less;
}
if (_lower > b._lower) {
return std::partial_ordering::greater;
}
if (_lower < b._lower) {
return std::partial_ordering::less;
}
return std::partial_ordering::equivalent;
}
uint_128_t& uint_128_t::operator&=(const uint_128_t& other) {
_upper &= other._upper;
_lower &= other._lower;
return *this;
}
uint_128_t& uint_128_t::operator|=(const uint_128_t& other) {
_upper |= other._upper;
_lower |= other._lower;
return *this;
}
uint_128_t& uint_128_t::operator^=(const uint_128_t& other) {
_upper ^= other._upper;
_lower ^= other._lower;
return *this;
}
uint_128_t uint_128_t::operator&(const uint_128_t& b) const {
uint_128_t a(*this);
a &= b;
return a;
}
uint_128_t uint_128_t::operator|(const uint_128_t& b) const {
uint_128_t a(*this);
a |= b;
return a;
}
uint_128_t uint_128_t::operator^(const uint_128_t& b) const {
uint_128_t a(*this);
a ^= b;
return a;
}
uint_128_t uint_128_t::operator~() const {
uint_128_t a;
a._upper = ~_upper;
a._lower = ~_lower;
return a;
}
uint_128_t operator&(uint_128_t&& a, const uint_128_t& b) {
return a &= b;
}
uint_128_t operator|(uint_128_t&& a, const uint_128_t& b) {
return a |= b;
}
uint_128_t operator^(uint_128_t&& a, const uint_128_t& b) {
return a ^= b;
}
uint_128_t& uint_128_t::operator<<=(unsigned __int64 index) {
unsigned char i = static_cast<unsigned char>(index);
unsigned char limit = 64;
if (i == limit) {
_upper = _lower;
_lower = 0;
}
else if (i > limit) {
_upper = _lower << (i - limit);
_lower = 0;
}
else {
auto mask = _lower >> (limit - i);
_upper = (_upper << i) | mask;
_lower <<= i;
}
return *this;
}
uint_128_t& uint_128_t::operator>>=(unsigned __int64 index) {
unsigned char i = static_cast<unsigned char>(index);
unsigned char limit = 64;
if (i == limit) {
_lower = _upper;
_upper = 0;
}
else if (i > limit) {
_lower = _upper >> (i - limit);
_upper = 0;
}
else {
auto mask = _upper >> (limit - i);
_lower = (_lower >> i) | mask;
_upper >>= i;
}
return *this;
}
uint_128_t uint_128_t::operator<<(unsigned __int64 index) const {
uint_128_t a(*this);
a <<= index;
return a;
}
uint_128_t uint_128_t::operator>>(unsigned __int64 index) const {
uint_128_t a(*this);
a >>= index;
return a;
}
uint_128_t operator<<(uint_128_t&& a, unsigned __int64 index) {
return a <<= index;
}
uint_128_t operator>>(uint_128_t&& a, unsigned __int64 index) {
return a >>= index;
}
uint_128_t& uint_128_t::operator+=(const uint_128_t& other) {
unsigned __int64 n = _lower;
_lower += other._lower;
_upper += other._upper;
if (_lower < n) {
_upper += 1;
}
return *this;
}
uint_128_t& uint_128_t::operator-=(const uint_128_t& other) {
unsigned __int64 n = _lower;
_lower -= other._lower;
_upper -= other._upper;
if (other._lower > n) {
_upper -= 1;
}
return *this;
}
uint_128_t& uint_128_t::operator*=(const uint_128_t& other) {
unsigned __int64 carry;
_lower = _umul128(_lower, other._lower, &carry);
_upper = carry;
return *this;
}
uint_128_t& uint_128_t::operator/=(const uint_128_t& other) {
unsigned __int64 carry = 0;
_lower = _udiv128(_upper, _lower, other._lower, &carry);
_upper = carry;
return *this;
}
uint_128_t& uint_128_t::operator%=(const uint_128_t& other) {
unsigned __int64 carry = 0;
_lower = _udiv128(_upper, _lower, other._lower, &carry);
_lower = carry;
_upper = 0;
return *this;
}
uint_128_t uint_128_t::operator+(const uint_128_t& b) const {
uint_128_t a(*this);
a += b;
return a;
}
uint_128_t uint_128_t::operator-(const uint_128_t& b) const {
uint_128_t a(*this);
a -= b;
return a;
}
uint_128_t uint_128_t::operator*(const uint_128_t& b) const {
uint_128_t a(*this);
a *= b;
return a;
}
uint_128_t uint_128_t::operator/(const uint_128_t& b) const {
uint_128_t a(*this);
a /= b;
return a;
}
uint_128_t uint_128_t::operator%(const uint_128_t& b) const {
uint_128_t a(*this);
a %= b;
return a;
}
uint_128_t&& operator+(uint_128_t&& a, const uint_128_t& b) {
a += b;
return std::move(a);
}
uint_128_t&& operator-(uint_128_t&& a, const uint_128_t& b) {
a -= b;
return std::move(a);
}
uint_128_t&& operator*(uint_128_t&& a, const uint_128_t& b) {
a *= b;
return std::move(a);
}
uint_128_t&& operator/(uint_128_t&& a, const uint_128_t& b) {
a /= b;
return std::move(a);
}
uint_128_t&& operator%(uint_128_t&& a, const uint_128_t& b) {
a %= b;
return std::move(a);
}
uint_128_t& uint_128_t::operator++() {
uint_128_t one(1);
operator+=(one);
return *this;
}
uint_128_t uint_128_t::operator++(int) {
uint_128_t a(*this);
operator++();
return a;
}
uint_128_t& uint_128_t::operator--() {
uint_128_t one(1);
operator-=(one);
return *this;
}
uint_128_t uint_128_t::operator--(int) {
uint_128_t a(*this);
operator--();
return a;
}
unsigned __int64 uint_128_t::upper() const {
return _upper;
}
unsigned __int64 uint_128_t::lower() const {
return _lower;
}
#endif
}
__int64
defined? Looks like a compiler specific keyword. I don't think it is standard C++. \$\endgroup\$#ifdef
s though. I don't have much experience using them in code definition, as they are used here. In a previous post, my use of them had been incorrect, due to a knowledge gap on my part.__int64
is compiler specific to Microsoft's compiler. The reason it is being used is that is what the intrinsic MS specific function use. Hence since the class relies on them, I want it to be apparent it is compiler specific. \$\endgroup\$