如何将字符串转换为任意长度的整数

How to convert a string into an arbitrary length integer

本文关键字:任意长 整数 转换 字符串      更新时间:2023-10-16

我正在做一个用C++实现多精度算术的项目。我在第一个栏上就摔倒了。我正试图将c字符串转换为整数的二进制表示,该整数可能包含比int所能容纳的比特更多的比特(理论上这可能是任意数量的比特)。我本质上想创建一个长数组,它将保存字符串中包含的数字的二进制表示,索引0是数字的最低有效"分支"。我假设这个数字是以10为基数的。

我已经研究过使用GMP中的代码,但它对我的需求来说是不必要的复杂,并且有大量依赖于平台的代码。

任何帮助都会很棒!如果你需要更多的细节,请告诉我。

就像@StevenJessop说的

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 != '' ) {
*this *= 10;
*this += *string - '0';
string++;
}
}

您想要做的第一件事就是拥有一个可工作的测试引擎。这是一个脑死亡,易于理解,任意精度的算术引擎。

这台发动机的用途有几个方面。首先,它使字符串转换为任意精度整数变得非常容易。其次,这是一种测试你后来改进的发动机的方法。即使它真的很慢,你也会更确信它是正确的(拥有两个独立的实现意味着一个实现中的角情况错误可能会被另一个捕获,即使你对其中任何一个都不更有信心)。

假设short至少为16位,char至少为8位(如果编译器支持,请使用实际的int_8样式类型)

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;
}
};

这是一个快速而肮脏的任意精度整数类型(甚至还没有编译),具有糟糕的性能。测试上面的东西,说服自己它是坚固的,然后从那里开始建立。

您的大部分代码都可以将实际的BigNum类作为模板参数,因此您可以对两个不同的实现执行相同的算法,并将结果进行比较以进行测试。

哦,还有另一条建议——编写一个模板类,通过CRTP"改进"一个基本的任意精度库。目标是只需要编写*=+=unary -,可能还有/=和一些shift_helpercompare_helper函数,并让模板自动为您编写其余方法。通过将样板放在一个位置,可以更容易地维护BigNum类的多个版本:并且拥有多个版本对于测试来说非常重要。