关于C#:如何将字符串转换为任意长度的整数

How to convert a string into an arbitrary length integer

我正在一个项目中实现C语言中的多精度算法。我有点跌倒了。我正在尝试将c字符串转换为整数的二进制表示形式,该整数可能包含比int可以容纳的位数更多的位(理论上,这可以是任意数量的位)。我本质上想创建一个long数组,该数组将容纳字符串中包含的数字的二进制表示形式,索引0是该数字的最低有效"四肢"。我假设数字以10为底。

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

任何帮助都会很棒!如果您需要更多详细信息,请告诉我。


就像@SteveJessop说的那样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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++;
    }
}


您要做的第一件事就是拥有一个运行正常的测试引擎。这是一个脑筋急转弯,易于理解,任意精度的算术引擎。

此引擎的用途有几个方面。首先,它使将字符串转换为任意精度的整数变得非常容易。其次,这是测试您以后改进的引擎的一种方法。即使它真的很慢,您也会确信它是正确的(并且拥有两个独立的实现意味着即使其中一个都不那么有信心,另一个实现中也可能会遇到极端的错误)。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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类的一个以上版本:并且具有多个版本对于测试目的非常重要。