Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binary string to cpp_int #297

Open
aaangeletakis opened this issue Feb 11, 2021 · 8 comments
Open

Binary string to cpp_int #297

aaangeletakis opened this issue Feb 11, 2021 · 8 comments

Comments

@aaangeletakis
Copy link

aaangeletakis commented Feb 11, 2021

Hello, I noticed boost multiprecision does not have a binary string to decimal converter, so I made one. Hope you enjoy!

// credit: Alexios A Angel
// C++ program to convert binary to decimal
#include <iostream>
#include <boost/multiprecision/cpp_int.hpp>

/******************************************************
 * Function : cpp_int bin_2_dec(const std::string_view& num)
 * Input    : A string of ones and zeros
 * Output   : cpp_int
 ******************************************************
 * Read digits left to right
 *  Ex.
 *                 MSB            LSB
 *               { '1', '1', '1', '0' }
 *   str index:     0    1    2    3
 *   bin index      3    2    1    0
 *
 *   The bit index is inverse (displayed above) to the
 *   digits in the string, so the algorithm will be
 *
 *   ┌──┬───────────────────────────────────────────────────────┐
 *   │1 │unsigned int bin_2_dec(const std::string_view& str)    │
 *   │2 │{                                                      │
 *   │3 │   unsigned int dec_value = 0;                         │
 *   │4 │   int          len       = str.size();                │
 *   │5 │   int          bin_index = len - 1;                   │
 *   │6 │   int          str_index = 0;                         │
 *   │7 │                                                       │
 *   │8 │   for(; str_index < len; ++str_index, --bin_index){   │
 *   │9 │      dec_value |= (str[str_index] - '0') << bin_index;│
 *   │10│   }                                                   │
 *   │11│   return dec_value;                                   │
 *   │12│}                                                      │
 *   └──┴───────────────────────────────────────────────────────┘
 *   You could say we are converting Little endian to Big endian. 
 *   Unfortunately, if there are more than sizeof(int) digits in the string than the shift in Line 9
 *   will overflow. In order to get around this in boost mp we 
 *   use boost::multiprecision::bit_set
 */

//change std::string_view to std::span<const char> if
//number of digits is greater than std::string_view{}.max_size()
boost::multiprecision::cpp_int bin_2_dec(const std::string_view& num)
{

    boost::multiprecision::cpp_int dec_value = 0;
    auto cptr = num.data();
    auto len  = num.size();
    //check if big enough to have 0b postfix
    if(num.size() > 2){
        //check if 2nd character is the 'b' binary postfix
        //skip over it & adjust length accordingly if it is
        if(num[1] == 'b' || num[1] == 'B'){
            cptr += 2;
            len  -= 2;
        }
    }

    //change i's type to cpp_int if the number of digits is greater
    //than std::numeric_limits<size_t>::max()
    for (size_t i = len - 1; 0 <= i; ++cptr, --i) {
        if(*cptr == '1'){
            boost::multiprecision::bit_set(/*.val = */ dec_value, /*.pos = */ i);
        }
    }
    return dec_value;
}

// Driver program to test above function
int main()
{
                                  //266 bits
    std::cout << bin_2_dec(/*.num = */"0b11111111111111111111111111111111111111111111111111111111111111111"
                                      "1111111111111111111111111111111111111111111111111111111111111111111"
                                      "1111111111111111111111111111111111111111111111111111111111111111111"
                                      "1111111111111111111111111111111111111111111111111111111111111111111") << '\n';
}
@ckormanyos
Copy link
Member

This is an interesting post. Boost.Multiprecision is transitioning to C++11 for the next release. Literal binary strings, if memory serves, arrived in C++14. Boost.Multiprecision is, however, not strictly tied to that exact language evolutionary step.

Does auto-detect of binary-string-to-multiprecision type make sense? And if so, for which (or all) or our backends?
Any interest in digit separators, or is this a separate issue altogether?

@jzmaddock
Copy link
Collaborator

There are really two features here:

  • iostream support for binary strings (which isn't in C++14 in fact, but no matter).
  • Literal/constexpr support via user defined literals.

The former is valid for all integer backends, the latter only for cpp_int, and need some hairy-scary modification to the existing user-defined-literal code ('tis horrible to write).

For iostream code, it should really be possible to write it in backend-agnostic format.

While we're at it, we should support ' separators in numbers as well.

@ckormanyos
Copy link
Member

There are really two features here

Indeed.
This seems like a larger operation. Points have been noted for potential future handling of this issue.

@aaangeletakis
Copy link
Author

aaangeletakis commented Feb 25, 2021

I would suggest it be based on std::bitset as it covers both of those requirements @jzmaddock .

#include <iostream>
#include <bitset>

int main(void)
{
    constexpr unsigned int myNum = 3;
    constexpr std::bitset< sizeof(myNum) * 8 > bits(myNum);
    std::cout << bits << std::endl;
    return 0;
}
//Compiled with -std=c++11
//Output: 00000000000000000000000000000011

As for arbitrary size literals, we can base it off of boost::dynamic_bitset

#include <iostream>
#include <boost/dynamic_bitset.hpp>

int main(void)
{
    unsigned int myNum = 3;
    boost::dynamic_bitset<> bits(sizeof(myNum) * 8, myNum);
    std::cout << bits << std::endl;
    return 0;
}

https://godbolt.org/z/PK3dxY

@aaangeletakis
Copy link
Author

Forgive my handwriting and spelling, but I believe an std::bin for binary strings support would look something like this:

Image of Yaktocat

@everdrone
Copy link

Would love to have the octal 0o123 as well! thanks for the code!

@jzmaddock
Copy link
Collaborator

Would love to have the octal 0o123 as well! thanks for the code!

Octal has always been supported, but it's 0123, there's no "o" letter in C++ octal strings.

@alan23273850
Copy link

Thanks for @aaangeletakis solution! One thing to note, the compiler warns me that "comparison of unsigned expression in ‘>= 0’ is always true." Maybe we can change the types of len and i to long long.

for (size_t i = len - 1; 0 <= i; ++cptr, --i) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants