c++c++11bigintbase-conversionstd-bitset

Convert a 74-bit integer to base 31


To generate a UFI number, I use a bitset of size 74. To perform step 2 of UFI generation, I need to convert this number:

9 444 732 987 799 592 368 290
(10000000000000000000000000000101000001000001010000011101011111100010100010)

into:

DFSTTM62QN6DTV1

by converting the first representation to base 31 and getting the equivalent chars from a table.

#define PAYLOAD_SIZE 74
// payload = binary of 9444732987799592368290
std::bitset<PAYLOAD_SIZE> bs_payload(payload);
/*
perform modulo 31 to obtain:
12(D), 14(F), 24(S), 25(T), 25, 19, 6, 2, 22, 20, 6, 12, 25, 27, 1
*/    

Is there a way to perform the conversion on my bitset without using an external BigInteger library?

Edit: I finally done a BigInteger class even if the Cheers and hth. - Alf's solution works like a charm


Solution

  • This code seems to work. To guarantee the result I think you need to do additional testing. E.g. first with small numbers where you can compute the result directly.

    Edit: Oh, now I noticed you posted the required result digits, and they match. Means it's generally good, but still not tested for corner cases.

    #include <assert.h>
    #include <algorithm>            // std::reverse
    #include <bitset>
    #include <vector>
    #include <iostream>
    using namespace std;
    
    template< class Type > using ref_ = Type&;
    
    namespace base31
    {
        void mul2( ref_<vector<int>> digits )
        {
            int carry = 0;
            for( ref_<int> d : digits )
            {
                const int local_sum = 2*d + carry;
                d = local_sum % 31;
                carry = local_sum / 31;
            }
            if( carry != 0 )
            {
                digits.push_back( carry );
            }
        }
    
        void add1( ref_<vector<int>> digits )
        {
            int carry = 1;
            for( ref_<int> d : digits )
            {
                const int local_sum = d + carry;
                d = local_sum % 31;
                carry = local_sum / 31;
            }
            if( carry != 0 )
            {
                digits.push_back( carry );
            }
        }
    
        void divmod2( ref_<vector<int>> digits, ref_<int> mod )
        {
            int carry = 0;
            for( int i = int( digits.size() ) - 1; i >= 0; --i )
            {
                ref_<int> d = digits[i];
                const int divisor = d + 31*carry;
                carry = divisor % 2;
                d = divisor/2;
            }
            mod = carry;
            if( digits.size() > 0 and digits.back() == 0 )
            {
                digits.resize( digits.size() - 1 );
            }
        }
    }
    
    
    int main() {
        bitset<74> bits(
            "10000000000000000000000000000101000001000001010000011101011111100010100010"
            );
        vector<int> reversed_binary;
        for( const char ch : bits.to_string() ) { reversed_binary.push_back( ch - '0' ); }
    
        vector<int> base31;
        for( const int bit : reversed_binary )
        {
            base31::mul2( base31 );
            if( bit != 0 )
            {
                base31::add1( base31 );
            }
        }
    
        { // Check the conversion to base31 by converting back to base 2, roundtrip:
            vector<int> temp31 = base31;
            int mod;
            vector<int> base2;
            while( temp31.size() > 0 )
            {
                base31::divmod2( temp31, mod );
                base2.push_back( mod );
            }
            reverse( base2.begin(), base2.end() );
            cout << "Original     : " << bits.to_string() << endl;
            cout << "Reconstituted: ";
            string s;
            for( const int bit : base2 ) { s += bit + '0'; cout << bit; };  cout << endl;
            assert( s == bits.to_string() );
        }
    
        cout << "Base 31 digits (msd to lsd order): ";
        for( int i = int( base31.size() ) - 1; i >= 0; --i )
        {
            cout << base31[i] << ' ';
        }
        cout << endl;
    
        cout << "Mod 31 = " << base31[0] << endl;
    }
    

    Results with MinGW g++:

    Original     : 10000000000000000000000000000101000001000001010000011101011111100010100010
    Reconstituted: 10000000000000000000000000000101000001000001010000011101011111100010100010
    Base 31 digits (msd to lsd order): 12 14 24 25 25 19 6 2 22 20 6 12 25 27 1
    Mod 31 = 1