4
\$\begingroup\$

Below is the updated binary mathematics class from the feedback provided here.

It was decided that the project should simply be written with an expectation of using C++20 as the minimum version to use.

The template was removed. Once removed I found there was a binary overflow happening in a bit shift that was masked by the template. Lesson learned. Make what you need now, and don’t worry about what you may need later.

Added friend overloads for move semantics on operators. Please let me know if additional changes are needed.

Converted the comp method to the <=> operator.

I had been under the impression that the compiler would automatically create the needed move, and assignment operator and constructor overloads. This post clarified the misunderstanding on my part.

I am keeping twos compliment. It is primarily used later as a method for confirming commutations done using other methods through tests. It really isn’t used otherwise.

There are additional classes for whole number, up through complex numbers. But I need to cascade what was learned here to them before I post them. If there is no additional feedback, I will create a new post for whole numbers next, and go from there.

note - If this wasn't the right way to follow up on the class, please let me know. :-)

binary_register.h
#include <bitset>
#include <limits>
#include <vector>

#include "../support_files/Type_Defines.h"

namespace Olly {

    /********************************************************************************************/
    //
    //                                 'binary_register' class
    //
    //        The binary_register class implements a series of binary registers sized to
    //        the integral type passed during to the template at definition.
    //
    //        Support for all of the binary operation is provides, along with binary
    //        based mathematical operations.  The implementation is little endian.
    //
    /********************************************************************************************/

    class binary_register {

    public:
#if _WIN32 || _WIN64
#if _WIN64
        using        Word = unsigned long int;
        using Double_Word = unsigned long long int;
#else
        using        Word = unsigned int;
        using Double_Word = unsigned long int;
#endif
#endif

#if __GNUC__
#if __x86_64__ || __ppc64__
        using        Word = unsigned long int;
        using Double_Word = unsigned long long int;
#else
        using        Word = unsigned int;
        using Double_Word = unsigned long int;
#endif
#endif
        using Register = std::vector<Word>;

        static const Word        MASK = ~Word(0);
        static const std::size_t BITS = std::numeric_limits<Word>::digits;

        binary_register();
        binary_register(const Text& value, Text base = "10");
        binary_register(const std::size_t& size, Word value);
        explicit
        binary_register(const std::size_t& size);
        virtual ~binary_register();

        binary_register(binary_register&& obj)                 = default;
        binary_register(const binary_register& obj)            = default;
        binary_register& operator=(binary_register&& obj)      = default;
        binary_register& operator=(const binary_register& obj) = default;

        friend void swap(binary_register& first, binary_register& second);

        bool              is() const;                             // bool conversion.
        bool             all() const;                             // bool test for all bits being set to 1.
        std::size_t    count() const;                             // The count of bits set to 1.
        std::size_t lead_bit() const;                             // Return the lead bit.
        std::size_t last_bit() const;                             // Return the last bit.
        Word        lead_reg() const;                             // Return the leading register.
        Word        last_reg() const;                             // Return the last register.

        bool at_bit(std::size_t index) const;                     // Return the value of a bit at the index.

        Word& at_reg(std::size_t index);                          // Return the word at the indexed register.
        Word  at_reg(std::size_t index) const;

        Text to_string()          const;                          // Return a string representation at radix 10.
        Text to_string(Word base) const;                          // Return a string representation at radix 'base'.
        void to_string(Text_Stream& stream) const;                // Send a string representation to a stream_type.

        std::size_t size_bits() const;                            // Get the total size in bits of the register.
        std::size_t size_regs() const;                            // Get the total size of words in the register.

        binary_register& resize_regs(std::size_t size);           // Resize the register to 'size' number of elements. 
        binary_register& resize_regs(std::size_t size, Word n);

        binary_register& set();                                   // Set all bits to true.
        binary_register& set(std::size_t index);                  // Set a bit at 'index' to true.

        binary_register& reset();                                 // Set all bits to false.
        binary_register& reset(std::size_t index);                // Set a bit at 'index' to false.

        binary_register& flip();                                  // Flip the truth of every bit in the register.
        binary_register& flip(std::size_t index);                 // Flip the truth of a bit at 'index'.

        bool operator==(const binary_register& b) const;
        bool operator!=(const binary_register& b) const;

        std::partial_ordering operator<=>(const binary_register& b) const;

        binary_register& operator&=(const binary_register& other);
        binary_register& operator|=(const binary_register& other);
        binary_register& operator^=(const binary_register& other);

        binary_register operator&(const binary_register& b) const;
        binary_register operator|(const binary_register& b) const;
        binary_register operator^(const binary_register& b) const;
        binary_register operator~() const;

        friend binary_register operator&(binary_register&& a, const binary_register& b);
        friend binary_register operator|(binary_register&& a, const binary_register& b);
        friend binary_register operator^(binary_register&& a, const binary_register& b);

        binary_register& operator<<=(std::size_t index);
        binary_register& operator>>=(std::size_t index);

        binary_register operator<<(std::size_t index) const;
        binary_register operator>>(std::size_t index) const;

        friend binary_register operator<<(binary_register&& a, std::size_t index);
        friend binary_register operator>>(binary_register&& a, std::size_t index);

        binary_register& operator+=(const binary_register& other);
        binary_register& operator-=(const binary_register& other);
        binary_register& operator*=(const binary_register& other);
        binary_register& operator/=(const binary_register& other);
        binary_register& operator%=(const binary_register& other);

        binary_register operator+(const binary_register& b) const;
        binary_register operator-(const binary_register& b) const;
        binary_register operator*(const binary_register& b) const;
        binary_register operator/(const binary_register& b) const;
        binary_register operator%(const binary_register& b) const;

        friend binary_register operator+(binary_register&& a, const binary_register& b);
        friend binary_register operator-(binary_register&& a, const binary_register& b);
        friend binary_register operator*(binary_register&& a, const binary_register& b);
        friend binary_register operator/(binary_register&& a, const binary_register& b);
        friend binary_register operator%(binary_register&& a, const binary_register& b);

        binary_register& operator++();
        binary_register  operator++(int);

        binary_register& operator--();
        binary_register  operator--(int);

        template<typename I>
        Word to_integral() const;                // Cast the register to an integral of type T.

        binary_register  bin_comp() const;       // Return the binary compliment of the register.

        // Get both the qotient and the remainder of the regester divided by 'other'.
        void div_rem(binary_register& other, binary_register& qot, binary_register& rem) const;

        binary_register& trim();    // Remove all trailing zeros, from the register.  
                                    // But leave atleast one register, even if a value of zero.

    private:
        typedef std::bitset<BITS> single_prc_bitset;

        static const Word ONE = 1;

        Register _reg;

        void get_shift_index(std::size_t& index, std::size_t& reg_index, std::size_t& bit_index) const;

        void divide_remainder(const binary_register& x, binary_register y, binary_register& q, binary_register& r) const;

        Text get_string(Word base) const;

        void  left_shift_bits(std::size_t& word_index, std::size_t& bit_index);
        void right_shift_bits(std::size_t& word_index, std::size_t& bit_index);
    };

    /********************************************************************************************/
    //
    //                          'binary_register' template implimentation
    //
    /********************************************************************************************/

    template<typename I>
    binary_register::Word binary_register::to_integral() const {
        static_assert(std::numeric_limits<I>::is_integer, "Integral required.");

        if (!_reg.empty()) {

            auto bits_of_I = std::numeric_limits<I>::digits;

            if (bits_of_I >= BITS && !_reg.empty()) {

                return I(_reg.front());
            }

            I n = 0;

            for (int i = BITS / bits_of_I; i >= 0; i -= 1) {

                n <<= bits_of_I;
                n += at_reg(i);
            }

            return static_cast<Word>(n);
        }

        return I(0);
    }
}
binary_register.cpp
#include "binary_register.h"

namespace Olly {

    binary_register::binary_register() : _reg(1, 0) {
    }

    binary_register::binary_register(const std::size_t& size) : _reg((size > 0 ? size : 1), 0) {
    }

    binary_register::binary_register(const std::size_t& size, Word value) : _reg((size > 0 ? size : 1), value) {
    }

    binary_register::binary_register(const Text& value, Text base) : _reg(1, 0) {

        binary_register::Word base_radix = to<Word>(base);          // Get the base radix to use.

        binary_register radix(1, base_radix);      // Define a binary_register to act as the radix.

        for (auto i : value) {                     // Loop through each digit and add it to the binary_register.

            Text digit_str = "";

            digit_str.push_back(i);

            binary_register::Word n = to<Word>(digit_str);

            if (n < base_radix) {
                binary_register digit(1, n);

                operator*=(radix);
                operator+=(digit);
            }
        }
    }

    binary_register::~binary_register() {
    }

    void swap(binary_register& left, binary_register& right) {

        auto temp = std::move(left);

        left = std::move(right);
        right = std::move(temp);
    }

    bool binary_register::is() const {

        for (auto i : _reg) {

            if (i) {
                return true;
            }
        }
        return false;
    }

    bool binary_register::all() const {

        for (auto i : _reg) {

            if (i != MASK) {
                return false;
            }
        }
        return true;
    }

    std::size_t binary_register::count() const {

        std::size_t count = 0;

        for (const auto i : _reg) {

            auto n = i;

            while (n > 0) {

                if (n & 1) {
                    count += 1;
                }
                n >>= 1;
            }
        }

        return count;
    }

    std::size_t binary_register::lead_bit() const {

        std::size_t word_index = _reg.size();

        binary_register::Word mask = (ONE << (BITS - ONE));

        for (auto i = _reg.crbegin(); i != _reg.crend(); ++i) {
            word_index -= 1;

            auto a = *i;

            std::size_t bit_index = BITS;

            while (a) {

                if (a & mask) {
                    return bit_index + (word_index * BITS);
                }
                a <<= 1;
                bit_index -= 1;
            }
        }

        return 0;
    }

    std::size_t binary_register::last_bit() const {

        std::size_t word_index = 0;

        binary_register::Word mask = 1;

        for (auto i = _reg.cbegin(); i != _reg.cend(); ++i) {

            auto a = *i;

            std::size_t bit_index = 1;

            while (a) {

                if (a & mask) {
                    return bit_index + (word_index * BITS);
                }
                a >>= 1;
                bit_index += 1;
            }
            word_index += 1;
        }

        return 0;
    }

    binary_register::Word binary_register::lead_reg() const {

        if (_reg.empty()) {
            return Word(0);
        }

        return _reg.back();
    }

    binary_register::Word binary_register::last_reg() const {

        if (_reg.empty()) {
            return Word(0);
        }

        return _reg.front();
    }

    bool binary_register::at_bit(std::size_t index) const {

        std::size_t reg_index, bit_index;
        get_shift_index(index, reg_index, bit_index);

        if (reg_index < _reg.size()) {

            return _reg[reg_index] & (ONE << (bit_index - ONE));
        }

        return false;
    }

    binary_register::Word& binary_register::at_reg(std::size_t index) {

        if (index >= _reg.size()) {

            _reg.resize(index + 1, 0);
        }

        return _reg[index];
    }

    binary_register::Word binary_register::at_reg(std::size_t index) const {

        if (index < _reg.size()) {

            return _reg[index];
        }

        return Word(0);
    }

    Text binary_register::to_string() const {

        return to_string(10);
    }

    Text binary_register::to_string(Word base) const {

        if (base > 360) {
            return "Radix must be between 0 and 360.";
        }

        if (base == 0) {
            Text_Stream stream;

            to_string(stream);

            return stream.str();
        }

        if (!is()) {
            return "0";
        }

        return get_string(base);
    }

    void binary_register::to_string(Text_Stream& stream) const {

        std::size_t i = _reg.size();

        while (i-- > 1) {
            stream << "word[" << i << "] = " << single_prc_bitset(_reg[i]).to_string() << "\n";
        }
        stream << "word[" << 0 << "] = " << single_prc_bitset(_reg[i]).to_string();
    }

    std::size_t binary_register::size_bits() const {
        return _reg.size() * BITS;
    }

    std::size_t binary_register::size_regs() const {
        return _reg.size();
    }

    binary_register& binary_register::resize_regs(std::size_t size) {

        _reg.resize(size);

        return *this;
    }

    binary_register& binary_register::resize_regs(std::size_t size, Word n) {

        _reg.resize(size, n);

        return *this;
    }

    binary_register& binary_register::set() {

        for (auto i = _reg.begin(); i != _reg.end(); ++i) {
            *i = MASK;
        }

        return *this;
    }

    binary_register& binary_register::set(std::size_t index) {

        std::size_t reg_index, bit_index;
        get_shift_index(index, reg_index, bit_index);

        if (reg_index >= _reg.size()) {

            _reg.resize(reg_index + 1, 0);
        }

        _reg[reg_index] |= (ONE << (bit_index - ONE));

        return *this;
    }

    binary_register& binary_register::reset() {

        for (std::size_t i = 0, end = _reg.size(); i < end; i += 1) {
            _reg[i] = Word(0);
        }

        return *this;
    }

    binary_register& binary_register::reset(std::size_t index) {

        std::size_t reg_index, bit_index;
        get_shift_index(index, reg_index, bit_index);

        if (reg_index >= _reg.size()) {

            _reg.resize(reg_index + 1, 0);
        }

        _reg[reg_index] &= ~(1 << (bit_index - 1));

        return *this;
    }

    binary_register& binary_register::flip() {

        for (std::size_t i = 0, end = _reg.size(); i < end; i += 1) {
            _reg[i] = ~_reg[i];
        }

        return *this;
    }

    binary_register& binary_register::flip(std::size_t index) {

        std::size_t reg_index, bit_index;
        get_shift_index(index, reg_index, bit_index);

        if (reg_index >= _reg.size()) {

            _reg.resize(reg_index + 1, 0);
        }

        _reg[reg_index] ^= (1 << (bit_index - 1));

        return *this;
    }

    bool binary_register::operator==(const binary_register& b) const {
        return operator<=>(b) == std::partial_ordering::equivalent;
    }

    bool binary_register::operator!=(const binary_register& b) const {
        return operator<=>(b) != std::partial_ordering::equivalent;
    }

    std::partial_ordering binary_register::operator<=>(const binary_register& b) const {
        
        std::size_t i = size_regs() > b.size_regs() ? size_regs() : b.size_regs();

        while (i-- > 0) {

            auto x = at_reg(i);
            auto y = b.at_reg(i);

            if (x > y) {
                return std::partial_ordering::greater;
            }

            if (x < y) {
                return std::partial_ordering::less;
            }
        }

        return std::partial_ordering::equivalent;
    }

    binary_register& binary_register::operator&=(const binary_register& other) {

        if (_reg.size() < other._reg.size()) {

            _reg.resize(other._reg.size(), 0);
        }

        for (std::size_t i = 0, end = _reg.size(); i < end; i += 1) {
            _reg[i] &= other.at_reg(i);
        }

        return *this;
    }

    binary_register& binary_register::operator|=(const binary_register& other) {

        if (_reg.size() < other._reg.size()) {

            _reg.resize(other._reg.size(), 0);
        }

        for (std::size_t i = 0, end = _reg.size(); i < end; i += 1) {
            _reg[i] |= other.at_reg(i);
        }

        return *this;
    }

    binary_register& binary_register::operator^=(const binary_register& other) {

        if (_reg.size() < other._reg.size()) {

            _reg.resize(other._reg.size(), 0);
        }

        for (std::size_t i = 0, end = _reg.size(); i < end; i += 1) {
            _reg[i] ^= other.at_reg(i);
        }

        return *this;
    }

    binary_register binary_register::operator&(const binary_register& b) const {

        binary_register a(*this);

        a &= b;

        return a;
    }

    binary_register binary_register::operator|(const binary_register& b) const {

        binary_register a(*this);

        a |= b;

        return a;
    }

    binary_register binary_register::operator^(const binary_register& b) const {

        binary_register a(*this);

        a ^= b;

        return a;
    }

    binary_register binary_register::operator~() const {

        binary_register a = *this;

        for (std::size_t i = 0, end = a._reg.size(); i < end; i += 1) {
            a._reg[i] = ~a._reg[i];
        }

        return a;
    }

    binary_register operator&(binary_register&& a, const binary_register& b) {
        return a &= b;
    }

    binary_register operator|(binary_register&& a, const binary_register& b) {
        return a |= b;
    }

    binary_register operator^(binary_register&& a, const binary_register& b) {
        return a ^= b;
    }

    binary_register& binary_register::operator<<=(std::size_t index) {

        std::size_t word_index, bit_index;

        get_shift_index(index, word_index, bit_index);

        if (word_index) {
            _reg.insert(_reg.begin(), word_index, 0);
        }

        if (bit_index) {

            left_shift_bits(word_index, bit_index);
        }

        return *this;
    }

    binary_register& binary_register::operator>>=(std::size_t index) {

        std::size_t word_index, bit_index;

        get_shift_index(index, word_index, bit_index);

        if (word_index) {

            if (word_index < _reg.size()) {

                _reg.erase(_reg.begin(), _reg.begin() + word_index);
            }
            else {
                for (auto i = _reg.begin(), end = _reg.end(); i != end; ++i) {
                    *i = 0;
                }

                return *this;
            }
        }

        if (bit_index) {

            right_shift_bits(word_index, bit_index);
        }

        return *this;
    }

    binary_register binary_register::operator<<(std::size_t index) const {

        binary_register a(*this);

        a <<= index;

        return a;
    }

    binary_register binary_register::operator>>(std::size_t index) const {

        binary_register a(*this);

        a >>= index;

        return a;
    }

    binary_register operator<<(binary_register&& a, std::size_t index) {
        return a <<= index;
    }

    binary_register operator>>(binary_register&& a, std::size_t index) {
        return a >>= index;
    }

    binary_register& binary_register::operator+=(const binary_register& other) {

        binary_register b(other);
        binary_register c;

        while (b.is()) {

            c = (*this & b) << 1;

            *this ^= b;

            b = c;
        }

        return *this;
    }

    binary_register& binary_register::operator-=(const binary_register& other) {

        if (other >= *this) {
            return reset();
        }

        binary_register b = other;

        if (b.size_regs() < size_regs()) {
            b._reg.resize(size_regs(), 0);
        }

        b = b.bin_comp();

        b._reg.push_back(0);  // Add a word to handle the two's compliment overflow.

        *this += b;

        _reg.pop_back(); // Get rid of the two's compliment overflow.

        return *this;
    }

    binary_register& binary_register::operator*=(const binary_register& other) {

        *this = *this * other;

        return *this;
    }

    binary_register& binary_register::operator/=(const binary_register& other) {

        *this = *this / other;

        return *this;
    }

    binary_register& binary_register::operator%=(const binary_register& other) {

        *this = *this % other;

        return *this;
    }

    binary_register binary_register::operator+(const binary_register& b) const {

        binary_register a = *this;

        a += b;

        return a;
    }

    binary_register binary_register::operator-(const binary_register& b) const {

        binary_register a = *this;

        a -= b;

        return a;
    }

    binary_register binary_register::operator*(const binary_register& b) const {

        std::size_t count = 0;

        binary_register x;
        binary_register y = b;

        while (y.is()) {

            if (y.at_bit(1)) {
                x += (*this << count);
            }

            count += 1;
            y >>= 1;
        }

        return x;
    }

    binary_register binary_register::operator/(const binary_register& b) const {

        binary_register q;
        binary_register r = *this;

        divide_remainder(*this, b, q, r);

        return q;
    }

    binary_register binary_register::operator%(const binary_register& b) const {

        binary_register q;
        binary_register r = *this;

        divide_remainder(*this, b, q, r);

        return r;
    }

    binary_register operator+(binary_register&& a, const binary_register& b) {
        return a += b;
    }

    binary_register operator-(binary_register&& a, const binary_register& b) {
        return a -= b;
    }

    binary_register operator*(binary_register&& a, const binary_register& b) {
        return a *= b;
    }

    binary_register operator/(binary_register&& a, const binary_register& b) {
        return a /= b;
    }

    binary_register operator%(binary_register&& a, const binary_register& b) {
        return a &= b;
    }

    binary_register& binary_register::operator++() {

        binary_register one(1, 1);

        operator+=(one);

        return *this;
    }

    binary_register binary_register::operator++(int) {

        binary_register a(*this);

        operator++();

        return a;
    }

    binary_register& binary_register::operator--() {

        binary_register one(1, 1);

        operator-=(one);

        return *this;
    }

    binary_register binary_register::operator--(int) {

        binary_register a(*this);

        operator--();

        return a;
    }

    binary_register binary_register::bin_comp() const {

        binary_register a = ~*this;
        binary_register one(1, 1);

        a += one;

        return a;
    }

    void binary_register::div_rem(binary_register& other, binary_register& qot, binary_register& rem) const {

        rem = *this;

        divide_remainder(*this, other, qot, rem);
    }

    binary_register& binary_register::trim() {

        while (!_reg.empty() && _reg.back() == 0) {

            _reg.pop_back();
        }

        if (_reg.empty()) {

            _reg.push_back(0);
        }

        return *this;
    }

    void binary_register::get_shift_index(std::size_t& index, std::size_t& reg_index, std::size_t& bit_index) const {

        if (index) {

            if (index >= BITS) {

                reg_index = index / BITS;
                bit_index = index % BITS;

                if (!bit_index) {
                    --reg_index;
                    bit_index = BITS;
                }

                return;
            }

            reg_index = 0;
            bit_index = index;

            return;
        }

        reg_index = 0;
        bit_index = 0;
    }

    void binary_register::divide_remainder(const binary_register& x, binary_register y, binary_register& q, binary_register& r) const {

        if (!y.is() || !x.is() || x < y) {
            return;
        }

        std::size_t lead_x = x.lead_bit();
        std::size_t lead_y = y.lead_bit();

        std::size_t bit_dif = (lead_x - lead_y);

        y <<= bit_dif;

        bit_dif += 2;

        while (bit_dif-- > 1) {

            if (r >= y) {
                q.set(bit_dif);
                r -= y;
            }
            y >>= 1;
        }
    }

    Text binary_register::get_string(Word base) const {

        binary_register radix(1, base);
        binary_register n = *this;

        Text_Stream stream;

        while (n.is()) {

            binary_register q;
            binary_register r = n;

            divide_remainder(n, radix, q, r);

            n = q;

            stream << r.at_reg(0);
        }

        Text res = stream.str();
        std::reverse(res.begin(), res.end());

        return res;
    }

    void binary_register::left_shift_bits(std::size_t& word_index, std::size_t& bit_index) {

        std::size_t i = _reg.size();

        _reg.push_back(0);

        auto bit_mask = Double_Word(MASK);

        while (i-- > 0) {

            auto buffer = Double_Word();
            buffer |= Double_Word(_reg[i]);

            buffer <<= bit_index;

            _reg[i] = static_cast<Word>(buffer & bit_mask);

            buffer >>= BITS;
            buffer |= Double_Word(_reg[i + 1]);

            _reg[i + 1] = static_cast<Word>(buffer);
        }

        if (_reg.back() == 0) {
            _reg.pop_back();
        }
    }

    void binary_register::right_shift_bits(std::size_t& word_index, std::size_t& bit_index) {

        bool pop_back = word_index ? false : true;

        if (word_index) {
            word_index -= 1;
        }
        _reg.push_back(0);

        auto inv_index = BITS - bit_index;

        std::size_t end = (_reg.size() - 1);

        auto bit_mask = Double_Word(MASK);

        for (std::size_t i = 0; i < end; i += 1) {

            auto buffer = Double_Word();
            buffer |= Double_Word(_reg[i + 1]);

            buffer <<= inv_index;

            _reg[i] >>= bit_index;
            _reg[i] |= static_cast<Word>(buffer & bit_mask);
        }
        _reg[end] >>= bit_index;


        while (word_index-- > 0) {
            _reg.push_back(0);
        }

        if (pop_back) {
            _reg.pop_back();
        }
    }
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Use fixed-width integer types

Your type aliases for Word and Double_Word are wrong. They may work on your platform, but there is no guarantee that a Double_Word is larger than a Word on all platforms. I recommend that you use the standard fixed-width integer types instead, like std::uint32_t and std::uint64_t.

There are also problems with your #ifdefs: consider for example that one can build Windows binaries using the GNU compiler, so both _WIN32 and __GNUC__ would be set simultaneously.

Naming

I would reconsider using the word "Register" here. In the context of computers, register has a specific meaning. A vector of integers is not a register.

Since the goal is to have a vector where you can access and manipulate individual bits, I think a better name would be bitvector. The standard library already has a type std::bitset which has similar functionality, except that its size is fixed, and your type has a dynamic size like std::vector.

There are also type aliases defined somewhere that you didn't include in the code you posted, like Text and Text_Stream. Are those std::string and std::stringstream? I strongly suggest that you do not create aliases for standard types, it makes it very hard for someone else reading your code to know what is going on, and they'd have to search your code base for the definition of those aliases. Just use std::string and std::stringstream.

Bits or words?

Some member functions work on both bits and words, some don't. Any calling code would need to check sizeof(binary_register::Word) to safely know how big a "word" is. I would simplify things and not expose Word at all in the public API, only allow bit-wise access or let the caller provide an integer type if they want to access multiple bits at once. For example, to_integral<I>() should just remove an I instead of a Word.

Avoid using strings to pass integers

The constructor has an overload that takes a string as input. While this is fine for value to support very large integers, why is base passed as a string? It's immediately converted back to a Word. Why not pass it as a regular integer instead?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ On GCC and clang with a 64-bit target, this probably wants to store 64bit words and convert to unsigned __int128 for multiplication. On MSVC for x86 or ARM64, the code might want to call __umulh from <intrin.h> to get the upper word of the product of two unsigned 64-bit multiplicands. either way, it doesn’t need Double_Word at all. \$\endgroup\$
    – Davislor
    Apr 14, 2023 at 18:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.