简体   繁体   中英

How to convert a string into an arbitrary length integer

I'm working on a project to implement multi-precision arithmetic in C++. I've sort of fallen at the first hurdle. I'm trying to convert a c-string into the binary representation of an integer that will probably contain more bits than an int can hold (this may be an arbitrary number of bits in theory). I essentially want to create an array of longs that will hold the binary representation of the number contained in the string, with index 0 being the least significant "limb" of the number. I am assuming that the number is in base ten.

I have already looked into using code from GMP but it is needlessly complex for my needs and has huge amounts of platform dependent code.

Any help would be great! If you require more details let me know.

Like @SteveJessop said

class Number {
public:
    Number();
    void FromString( const char * );
    void operator *= ( int );
    void operator += ( int );
    void operator = ( int );
}

Number::FromString( const char * string )
{
    *this = 0;
    while( *string != '\0' ) {
        *this *= 10;
        *this += *string - '0';
        string++;
    }
}

The first thing you want to do is have a working test engine. This is a brain-dead, easy to understand, arbitrary precision arithmetic engine.

The purpose of this engine is a few fold. First, it makes converting strings into arbitrary precision integers really easy. Second, it is a means to test your later, improved engines. Even if it is really slow, you'll be more convinced it is correct (and having two independent implementations means corner case errors in one might be caught in another, even if you aren't more confident in either).

Assumes short is at least 16 bits and char is at least 8 (use the actual int_8 style types if your compiler supports them)

short Add(unsigned char left, unsigned char right, unsigned char extra=0) { return unsigned short(left)+unsigned short(right)+unsigned short(extra); }
unsigned short Multiply(unsigned char left, unsigned char right) { return unsigned short(left)*unsigned short(right); }
std::pair<unsigned char,unsigned char> CarryCalc(unsigned short input) {
  std::pair<unsigned char,unsigned char> retval;
  retval.first = input & (1<<8-1);
  retval.second = input>>8;
  return retval;
}
struct BigNum {
  std::vector<char> base256;
  BigNum& operator+=( BigNum const& right ) {
    if (right.base256.size() > base256.size())
      base256.resize(right.base256.size());
    auto lhs = base256.begin();
    auto rhs = right.base256.begin();
    char carry = 0;
    for(; rhs != right.base256.end(); ++rhs, ++lhs) {
      auto result = CarryCalc( Add( *lhs, *rhs, carry ) );
      *lhs = result.first;
      carry = result.second;
    }
    while( carry && lhs != base256.end() ) {
      auto result = CarryCalc( Add( *lhs, 0, carry ) );
      *lhs = result.first;
      carry = result.second;
    }
    if (carry)
      base256.push_back(carry);
    return *this;
  }
  BigNum& scaleByChar( unsigned char right ) {
    char carry = 0;
    for(auto lhs = base256.begin(); lhs != base256.end(); ++lhs) {
      unsigned short product = Multiply( *lhs, right );
      product += carry;
      auto result = CarryCalc( product );
      *lhs = result.first;
      carry = result.second;
    }
    if (carry)
      base256.push_back(carry);        
    return *this;
  }
  BigNum& shiftRightBy8BitsTimes( unsigned int x ) {
    if (x > base256.size()) {
      base256.clear();
      return *this;
    }
    base256.erase( base256.begin(), base256.begin()+x) )
    return *this;
  }
  // very slow, O(x * n) -- should be O(n) at worst
  BigNum& shiftLeftBy8BitsTimes( unsigned int x ) {
    while( x != 0 ) {
      base256.insert( base256.begin(), 0 );
      --x;
    }
    return *this;
  }
  // very slow, like O(n^3) or worse (should be O(n^2) at worst, fix shiftLeft)
  BigNum& operator*=( BigNum const& right ) {
    unsigned int digit = 0;
    BigNum retval;
    while (digit < right.base256.size()) {
      BigNum tmp = *this;
      tmp.shiftLeftBy8BitsTimes( digit );
      tmp.scaleByChar( right.base256[digit] );
      retval += tmp;
      ++digit;
    }
    *this = retval;
    return *this;
  }
};

which is a quick and dirty arbitrary precision integer type (not even compiled yet) with horrible performance. Test something like the above, convince yourself it is solid, then build up from there.

Much of your code could take the actual BigNum class in question as a template argument, so you can do the same algorithm with two different implementations, and compare the results for testing purposes.

Oh, and another piece of advice -- write a template class that "improves" a bare-bones arbitrary precision library via CRTP. The goal is to only have to write *= , += , unary - , and maybe /= and some shift_helper and compare_helper functions, and have the rest of your methods automatically written for you by the template. By putting the boilerplate in one spot it makes it easier to maintain more than one version of your BigNum class: and having more than one version is very important for testing purposes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM