【问题标题】:Why do BigInteger implementations use sign-magnitude instead of two's complement?为什么 BigInteger 实现使用符号幅度而不是二进制补码?
【发布时间】:2019-02-02 16:46:52
【问题描述】:

任意精度的有符号整数几乎总是使用符号幅度表示来实现:

  • (Java) BigInteger 在 OpenJDK 中
  • (Python)Bigint CPython 中 Python 内置 int 类型的实现
  • (C) mpz_t 在 GMP 中,GNU 多精度算术库
  • (C++)BigInteger 在一个 bigint 库中,作者:Matt McCutchen
  • (Rust)BigInt 在 num-bigint 库中

对符号大小的明显偏好与固定宽度有符号整数类型中对二进制补码的近乎普遍偏好形成对比。问题是,为什么 BigIntegers 如此明显首选符号幅度?(如果您不同意这个前提,我欢迎反例。)

请注意,BigInteger API 通常为重要的按位运算指定“如同二进制补码”语义(例如JavaPython)。这提供了与这些操作的通常含义的一致性。这并不规定实际的内部表示(仅是一个实现细节),但如果其他所有条件都相同,它应该是支持在内部使用二进制补码的一点。

浮点数使用符号大小,与使用二进制补码的整数不同。不过,浮点在这里并不是真正的指导性先例,因为浮点运算的行为和算法与整数运算有很大不同。 Bignums 更像整数而不是浮点数。

我们知道为什么二进制补码在数学上起作用以及为什么它具有优势的“教科书”原因。在我看来,这些原因同样适用于整数和 BigInteger。这在多大程度上是真的?

当然,硬件固定精度整数和软件任意精度整数的设计约束之间存在巨大差异。从这个意义上说,看到设计师在这些不同的领域做出不同的权衡也就不足为奇了。那么,当应用于任意精度整数时,符号大小和二进制补码之间的权衡是什么?例如,这可能与某些重要算法的性能或简单性有关。

我希望您的回答能够阐明 BigInteger 算术的设计注意事项,并帮助我从新的角度重新审视我对二进制补码的了解。

(要清楚:当我说任意精度整数的二进制补码时,我的意思是使用单词数组的表示,其位模式放在一起时,是所需数字的二进制补码表示 - 也许附加要求没有“不必要的前导 0”(对于非负数)或“不必要的前导 1”(对于负数)。)

【问题讨论】:

  • 我修复了 Python 的链接;您指向的链接指向浮点到字符串和字符串到浮点转换使用的专业且有些受限的 bigint 实现,与 Python 的 int 类型无关。

标签: math data-structures binary biginteger arbitrary-precision


【解决方案1】:

Two's Complement 使得等长数字的加减法更简单,但乘法和除法更复杂。对于硬件实现,可能会有时间损失,但并非总是如此。查看 X86“Ivy Bridge”指令表,唯一出现二进制补码需要更多时间的情况是 128 位有符号除数除以 64 位有符号除数。所以这主要是基于软件的数学问题。

大整数库可能对大数使用更复杂但更快的表示。以下是一些示例文章的链接:

https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic

https://cp-algorithms.com/algebra/big-integer.html

http://www.apfloat.org/ntt.html

对于相当大的数字,更复杂的方法通常更快,对于中等大小的数字,更简单的实现会更快。

【讨论】:

  • 二进制补码的优点可能也不太适用,因为具有由不同长度的位串表示的值(我认为对于不同长度的数字添加不会那么简单)。
  • @Dukeling - 我更新了我的答案,注意更简单的情况是等长数字。
  • 另外,一个人不必在符号大小中签署扩展数字。
  • FWIW,链接中提到的更复杂的表示通常仅用于特殊目的。例如。一个不做除法,另一个做加法或减法有很大的问题。大多数使用二进制基数(2^32 或 2^64)或十进制基数(例如 10^9 或 10^19)。
  • " 因为浮点运算的行为和算法与整数运算有很大不同"。实际上,最终,他们不是。 FP (或多或少)移位直到指数匹配,然后算术是相同的。只有一个额外的舍入和移位步骤。
【解决方案2】:

由于我很少构建自己的 bignum 库,因此我同意 rcgldr 的回答 (+1) 二进制补码会在高级操作中带来问题,而不仅仅是 *,/

除此之外,一些bignum 库不使用2 作为基础,使用二进制补码也是一种诡计。不使用2 的力量的原因是我们正在以10 为基础进行计算,因此我们期望输入并得到这样的结果。 conversion between base 2 (or power of 2) and base 10 是 IIRC ~O(n^2) 任务,对于非常大的数字,它通常比对它们执行的操作花费更多。所以库使用10 的最大功能,适合ALU 使用的字......例如在32位中它是1 000 000 000 这会稍微浪费空间,但可以简化数字和字符串之间的输入和输出转换对O(n) 的陈述。其中n 是使用的数字或单词数...

另外,二进制补码会使许多底层运算(如 multiplication by NTTmultiplication by NTT)所需的模运算复杂化

二进制补码处理和恢复将采用O(n),而单独的符号只需O(1),我认为这是主要原因。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2016-05-18
  • 1970-01-01
  • 1970-01-01
  • 2015-11-28
  • 2010-11-10
  • 2011-10-14
  • 1970-01-01
相关资源
最近更新 更多