【问题标题】:How to handle arbitrarily large integers如何处理任意大的整数
【发布时间】:2008-11-21 21:36:45
【问题描述】:

我正在研究一种编程语言,今天我可以编译阶乘函数(递归),但是由于整数的最大大小,我可以获得的最大整数是阶乘(12)。有哪些技术可以处理任意最大大小的整数。该语言目前通过将代码翻译成 C++ 来工作。

【问题讨论】:

  • 你要找的东西通常被称为“Bignum”,基本上是某种处理任意大整数的类
  • 您不能处理任意最大大小的整数,因为最大大小受可用存储空间的限制,并且没有计算机具有无限存储空间。您可以编写库来处理您可能需要的尽可能多的数字,但认为这些方法存在技术限制是值得评论的。
  • 话虽如此,你可以用 4GB 的 RAM(和 TB 的硬盘,如果你真的想要的话)存储一个非常大的数字,所以这只是一个哲学上的反对。

标签: c++ integer overflow long-integer integer-overflow


【解决方案1】:

如果您需要大于 32 位的数据,您可以考虑使用 64 位整数(long long),或者使用或编写任意精度的数学库,例如GNU MP.

【讨论】:

    【解决方案2】:

    如果您想推出自己的任意精度库,请参阅 Knuth 的半数字算法,他的巨著第 2 卷。

    【讨论】:

    • Knuth +1 -- 否则你可能会错过罕见的除法角落案例。
    【解决方案3】:

    如果您要将它构建成一种语言(我猜是为了学习目的),我想我可能会编写一个小的 BCD 库。只需将您的 BCD 数字存储在字节数组中即可。

    事实上,凭​​借当今巨大的存储能力,您可能只需使用一个字节数组,其中每个字节仅包含一个数字 (0-9)。然后,您编写自己的例程来对字节数组进行加减乘除。

    (除法是最难的,但我敢打赌你可以在某处找到一些代码。)

    我可以给你一些类似 Java 的伪代码,但目前还不能真正从头开始做 C++:

    class BigAssNumber {
        private byte[] value;
    
        // This constructor can handle numbers where overflows have occurred.
        public BigAssNumber(byte[] value) {
            this.value=normalize(value);
        }
    
        // Adds two numbers and returns the sum.  Originals not changed.
        public BigAssNumber add(BigAssNumber other) {
            // This needs to be a byte by byte copy in newly allocated space, not pointer copy!
            byte[] dest = value.length > other.length ? value : other.value;         
    
            // Just add each pair of numbers, like in a pencil and paper addition problem.
            for(int i=0; i<min(value.length, other.value.length); i++)
                dest[i]=value[i]+other.value[i];
    
            // constructor will fix overflows.
            return new BigAssNumber(dest);
        }
    
        // Fix things that might have overflowed  0,17,22 will turn into 1,9,2        
        private byte[] normalize(byte [] value) {
            if (most significant digit of value is not zero)
                extend the byte array by a few zero bytes in the front (MSB) position.
    
            // Simple cheap adjust.  Could lose inner loop easily if It mattered.
            for(int i=0;i<value.length;i++)
                while(value[i] > 9) {
                    value[i] -=10;
                    value[i+1] +=1;
                }
            }
        }
    }
    

    我利用我们在一个字节中有很多额外空间的事实来帮助以通用方式处理加法溢出。也可以用于减法,并有助于一些乘法。

    【讨论】:

    • 如果你不是为了上学,就去某个地方获取一些 BigInteger 代码并使用它。
    【解决方案4】:

    在 C++ 中没有简单的方法可以做到这一点。您必须使用外部库,例如 GNU Multiprecision,或者使用其他本机支持任意大整数的语言,例如 Python。

    【讨论】:

    • 我认为这很容易。 GMP 带有一个不错的 C++ 标头
    【解决方案5】:

    其他海报提供了可以为您执行此操作的库的链接,但您似乎正在尝试将其构建到您的语言中。我的第一个想法是:你确定你需要这样做吗?正如其他人所建议的那样,大多数语言都会使用附加库。

    假设您正在编写一个编译器并且您确实需要此功能,您可以为汇编中的任意大值实现整数算术函数。

    例如,一个简单(但非最佳)的实现会将数字表示为二进制编码的十进制。算术函数可以使用与使用铅笔和纸进行数学运算时使用的算法相同的算法。

    另外,考虑为这些大整数使用专门的数据类型。这样,“普通”整数就可以使用标准的 32 位算术。

    【讨论】:

    • 人们对 BCD 的痴迷是什么? Nodoby 在这里要求的。
    【解决方案6】:

    我的首选方法是将我当前的 int 类型用于 32 位整数(或者可能将其更改为内部为 long long 或类似的,只要它可以继续使用相同的算法),然后当它溢出,将其更改为存储为 bignum,无论是我自己创建的,还是使用外部库。但是,我觉得我需要检查每个算术运算的溢出,大约是算术运算的 2 倍开销。我该如何解决?

    【讨论】:

    • 不要太担心性能。对它进行编码而不考虑性能,然后如果你不能满足某些基准,就去重构东西。
    • 是的,您甚至可以将冒泡排序重构为归并排序...而且与其他人相比,您当然想要一个良好的营销形象,以便将您的通用 OO 语言的收缩包装盒出售给大男孩。什么?不是通用的吗?
    • 您描述的问题是我建议创建新数据类型的原因。如果乘法溢出,C++、Java 等不会自动将 16 位 int 转换为 32 位,那你为什么要这样做。另一方面,如果这是一个书面要求,您只需要接受性能损失。
    【解决方案7】:

    如果我正在实现自己的语言并希望支持任意长度的数字,我将使用带有进位/借位概念的目标语言。但是由于没有 HLL 可以在没有严重性能影响(如异常)的情况下实现这一点,我肯定会在汇编中实现它。可能需要一条指令(如 x86 中的 JC 中)来检查溢出并处理它(如 x86 中的 ADC 中),这对于实现任意精度的语言来说是可接受的折衷方案。然后我将使用一些用汇编语言编写的函数而不是常规运算符,如果您可以利用重载来获得更优雅的输出,那就更好了。但我不希望生成的 C++ 作为目标语言可维护(或打算维护)。

    或者,只需使用一个比您需要的花里胡哨更多的库,并将其用于您的所有数字。

    作为一种混合方法,检测汇编中的溢出并在溢出时调用库函数,而不是滚动您自己的迷你库。

    【讨论】:

      猜你喜欢
      • 2021-08-18
      • 1970-01-01
      • 2022-08-17
      • 1970-01-01
      • 2017-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-17
      相关资源
      最近更新 更多