我正在开发一个在C ++中实现多精度算术的项目。我有点陷入第一关。我正在尝试将一个c字符串转换为一个整数的二进制表示,该整数可能包含比int可以容纳的更多位(理论上这可能是任意数量的位)。我本质上想要创建一个longs数组,它将保存字符串中包含的数字的二进制表示,索引0是数字中最不重要的“肢体”。我假设这个数字在十点左右。
我已经考虑过使用GMP中的代码,但它对我的需求来说是不必要的复杂,并且拥有大量的平台相关代码。
任何帮助都会很棒!如果您需要更多详细信息,请告知我们。
答案 0 :(得分:3)
喜欢@SteveJessop说
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++;
}
}
答案 1 :(得分:2)
您要做的第一件事就是拥有一个有效的测试引擎。这是一个脑死亡,易于理解,随意精确的算术引擎。
这款发动机的目的有几个。首先,它使得将字符串转换为任意精度整数非常容易。其次,它是一种测试后期改进引擎的方法。即使它真的很慢,你也会更加确信它是正确的(并且有两个独立的实现意味着一个中的角落错误可能会被另一个错误捕获,即使你对它们也不是更有信心)。
假设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_helper
和compare_helper
函数,并且其余的方法由模板自动为您编写。通过将样板放在一个位置,可以更容易地维护BigNum
类的多个版本:并且具有多个版本对于测试目的非常重要。