任意精度无符号整数仅支持后增量运算符

Posted

技术标签:

【中文标题】任意精度无符号整数仅支持后增量运算符【英文标题】:Arbitrary precision unsigned integer supporting just post increment operator 【发布时间】:2013-12-21 13:12:02 【问题描述】:

我正在寻找一个非常简单的 C++ 类,它实现了一个具有任意精度的无符号整数和后自增运算符。

我知道有用于任意精度整数运算的库,但我的需求非常简单,我更愿意避免完整库的重量。

在我看来,我目前的实现还不够简单,不够优雅。你有什么建议?

#include <vector>
#include <string>
#include <algorithm>

class UNat

public:
    static const char base = 10;
    UNat( const char* n )
    
        std::string s(n);
        std::reverse(s.begin(),s.end());
        for ( size_t i = 0; i < s.length(); i++ ) 
            n_.push_back(s[i]-'0');
        
    

    UNat& operator++(int) 
    
        bool carry = false;
        bool finished = false;

        for ( size_t i = 0; i < n_.size() && !finished; i++ ) 
            n_[i] = add(n_[i],1,carry);
            if ( carry ) 
                finished = false;
             else 
                finished = true;
            
        
        if ( carry ) 
            n_.push_back(1);
        
        return *this;
    

    std::string to_string() const
    
        std::string r(n_.begin(), n_.end());
        std::reverse(r.begin(),r.end());
        std::for_each(r.begin(), r.end(), [](char& d)  d+='0';);
        return r;
    

private:
    char add( const char& a, const char& b, bool& carry )
    
        char cc = a + b;
        if ( cc >= base ) 
            carry = true;
            cc -= base;
         else 
            carry = false;
        
        return cc;
    
    std::vector< char > n_;    
;

std::ostream& operator<<(std::ostream& oss, const UNat& n)

    oss << n.to_string();
    return oss;


#include <iostream>

int main()

    UNat n("0");
    std::cout << n++ << "\n";
    std::cout << UNat("9")++ << "\n";
    std::cout << UNat("99")++ << "\n";
    std::cout << UNat("19")++ << "\n";
    std::cout << UNat("29")++ << "\n";
    std::cout << UNat("39")++ << "\n";
    return 0;

【问题讨论】:

后增量应该增加值并且产生原始值作为表达式结果。您的后增量运算符似乎产生了修改后的值。这是您需要帮助的问题吗? 我想如果你所做的只是递增,ripple-carry 还不错......在 32 位或 64 位原语上而不是 8 位上做事情可能会更好但 8 位确实保持简单。 @ebyrob:我从来没有找到对 Babbage 预测方案的良好描述,但 Pascal(在他之前)使用了一个很好的重力辅助方案。基本上每个数字轮都必须在添加之前手动启动。然后任何进位都会在下一个数字轮上扔东西,给它一个额外的推动,这些扔动作可以并行进行。很巧妙,很明显。除了我不明白它是如何取消测序的。 :( 如果你想做的只是增加它,你几乎不需要无限的精度。每次增量为 1ns,64 位长度至少需要 10^9 秒才能溢出。然后您可以使用 C++ long 和标准的后增量运算符。 @Ira: long 只保证是 32 位,所以long long 会更像。 【参考方案1】:

为了避免返回变异值,保存本地副本并返回:

UNat operator++(int) 

    UNat copy = *this;
    // ....
    return copy;
 // don't return by reference

相比之下,前缀运算符确实通过引用返回。

UNat& operator++ ()

  // ....
  return *this;


来自Arbitrary-precision arithmetic Explanation的一些提示和技巧:

1/ 数字相加或相乘时,预先分配最大空间 如果您发现它太多,则需要然后减少。例如, 添加两个 100-“digit”(其中 digit 是一个 int)数字永远不会给出 你超过 101 位数。将 12 位数字乘以 3 位数字 number 永远不会生成超过 15 位数字(添加数字计数)。

您的加法函数的替代实现如下所示:

c->lastdigit = std::max(a->lastdigit, b->lastdigit)+1;
carry = 0;

for (i=0; i<=(c->lastdigit); i++) 
    c->digits[i] = (char) (carry+a->digits[i]+b->digits[i]) % 10;
    carry = (carry + a->digits[i] + b->digits[i]) / 10;

【讨论】:

【参考方案2】:

代码实现了通常所说的二进制编码十进制 (BCD)。非常简单,正如您所说,如果您只想增加而不需要一般添加,则实现可以简单得多。为了进一步简化,请使用数字'0''9' 的内部字符表示,而不是值09。为了进一步简化,将字符存储在std::string

for (int index = 0; index < digits.size(); ++index) 
    if (digits[index] != '9') 
        ++digits[index];
        break;
    
    digits[index] = '0';

if (index == digits.size())
    digits.push_back('1');

这使得流插入器几乎是微不足道的。

【讨论】:

如果你想使用不同的基数,你可以在当前位值上使用 binary 和 just 和 if-then-else 来做到这一点。但这所做的只是制作了一个无限精度的慢版本。 @IraBaxter - 可以添加很多花里胡哨的东西。问题是如何让它变得简单,这是我能看到的最简单的。特别是,支持大于 10 的基数意味着编写更复杂的代码来增加单个元素。 支持大于 10 的基数是微不足道的。对于基数 r,使用值 0 到 r-1 的数字。您的 if 语句变为“digits[index]!=”。如果您选择 r=10^n(您选择 n=1),那么打印数字很容易。 @IraBaxter - 如果你这样做,流插入器会变得更加复杂;它不能再简单地复制现有的字符值。再说一遍:有很多东西可以以更复杂的代价添加。我的目标很简单,而不仅仅是简单。如果您有其他目标,请务必发布您自己的答案。 @Yakk - 高度权威的网站Wikipedia 不同意。它区分未压缩 BCD和压缩 BCD。

以上是关于任意精度无符号整数仅支持后增量运算符的主要内容,如果未能解决你的问题,请参考以下文章

使用 AVX 的有符号/无符号整数的最小值

如何将无符号整数加载到 SIMD 中

c 语言中除号仅用于整数间吗?

如何在 x86(32 位)程序集中将无符号整数转换为浮点数?

我将向量作为双精度传递给函数,但视觉工作室将其称为无符号整数

将负双精度转换为无符号整数的行为是不是在 C 标准中定义? ARM 与 x86 上的不同行为