编程语言如何处理大数算术
Posted
技术标签:
【中文标题】编程语言如何处理大数算术【英文标题】:How do programming languages handle huge number arithmetic 【发布时间】:2010-11-29 03:40:48 【问题描述】:对于使用 64 位处理器的计算机,它可以处理的最大数字是 264 = 18,446,744,073,709,551,616。编程语言(比如 Java 或 C、C++)如何处理高于此值的数字的算术运算。任何收银机都不能将其作为一个整体保存。这个问题是如何解决的?
【问题讨论】:
这是一个关于大整数的问题还是关于表示多大的浮点数的问题? 整数只是为了让这个概念像现在一样正确:) 我问了一个类似的问题,最终找到了我的方式; ***.com/questions/1218149/… 狩猎愉快! 【参考方案1】:有很多专门的技术可以对大于寄存器大小的数字进行计算。其中一些在arbitrary precision arithmetic的这篇***文章中进行了概述@
C 和 C++ 等低级语言将大量计算留给您选择的库。一个值得注意的是GNU Multi-Precision library。 Python等高级语言将其集成到语言的核心中,因此普通数字和非常大的数字对程序员来说是相同的。
【讨论】:
【参考方案2】:你假设错了。 在单个寄存器中可以处理的最大数字是 64 位数字。但是,使用一些智能编程技术,您可以将几十个这些 64 位数字连续组合在一起,以生成一个巨大的 6400 位数字,并使用它来进行更多计算。只是没有将数字放在一个寄存器中那么快。
即使是旧的 8 位和 16 位处理器也使用了这个技巧,他们只会让数字溢出到其他寄存器。它使数学变得更复杂,但并没有结束可能性。
但是,如此高精度的数学运算是极其不寻常的。即使你想计算美国的全部国债并将结果存储为津巴布韦元,我认为 64 位整数仍然足够大。不过,它绝对足够容纳我的储蓄账户的金额。
【讨论】:
not true.... 尤其是小数部分,他们不想快速四舍五入(例如使用普通浮点数时) @reinier,Donald Knuth 写了几本关于这个主题以及计算机和数学的好书和文章。我建议你开始阅读其中的一些。许多语言都有一个特殊的 BigNum 库,可以通过多种方式实现。 (见en.wikipedia.org/wiki/Bignum) 不稀奇..嗯怎么样..说我想要35!..很容易越界【参考方案3】:处理真正大量数字的编程语言使用自定义数字原语,这些原语超出了为 32、64 或 128 位 CPU 优化的正常操作。这些数字在计算机安全和数学研究中特别有用。
GNU Multiple Precision Library 可能是这些方法中最完整的示例。
您可以使用数组来处理更大的数字。在您的网络浏览器中尝试一下。在 Web 浏览器的 javascript 控制台中键入以下代码:
JavaScript 失败的地方
console.log(9999999999999998 + 1)
// expected 9999999999999999
// actual 10000000000000000 oops!
JavaScript 不处理9999999999999998
以上的纯整数。但是编写自己的数字原语是为了使这个计算工作足够简单。这是使用a custom number adder class in JavaScript 的示例。
使用自定义数字类通过测试
// Require a custom number primative class
const Num = require('./bases')
// Create a massive number that JavaScript will not add to (correctly)
const num = new Num(9999999999999998, 10)
// Add to the massive number
num.add(1)
// The result is correct (where plain JavaScript Math would fail)
console.log(num.val) // 9999999999999999
工作原理
您可以查看class Num ... 的代码以查看正在发生的事情的详细信息;但这里是使用逻辑的基本概述:
类:
Num
类包含单个 Digit
类的数组。
Digit
类包含单个数字的值,以及处理Carry flag
的逻辑
步骤:
-
选择的数字变成字符串
每个数字都转换为
Digit
类并作为数字数组存储在Num
类中
当Num
递增时,它会被传送到数组中的第一个Digit
(最右边的数字)
如果Digit
值加上Carry flag
等于Base
,则调用左边的下一个Digit
递增,并将当前编号重置为0
...一直重复到数组最左边的数字
从逻辑上讲,它与机器级别发生的事情非常相似,但在这里它是无限的。你可以read more about about how digits are carried here;这可以应用于任何基数。
【讨论】:
【参考方案4】:Ada 实际上本机支持这一点,但仅限于它的无类型常量(“命名数字”)。对于实际变量,您需要找到一个任意长度的包。见Arbitrary length integer in Ada
【讨论】:
+1 很高兴在 SO 上见到你,T.E.D. 我只是注意到 Ada 支持它作为内置的,在其他地方阅读,但不知道它仅限于常量。这有点奇怪。你甚至可以取一个常量 bignum + 一个小的 int,并最终得到一个 bignum 结果吗?【参考方案5】:和你差不多。在学校里,你记住了个位数的加法、乘法、减法和除法。然后,您学习了如何将多位数问题作为单位数问题的序列来处理。
如果您愿意,您可以将两个 20 位数字相乘,只需了解简单的算法和一位数的乘法表即可。
【讨论】:
这是真的,就目前而言。然而,它只完成了最基本的操作:加法、减法、乘法和除法只是开始。然后,要真正处理好任意数字,您必须担心平方根、精确跟踪等。这就是为什么我认为每种语言都应该提供一个好的、完整的实现作为标准。【参考方案6】:一般来说,语言本身不处理高精度、高精度的大数运算。 库更有可能是编写的,它使用替代的数值方法来执行所需的操作。
例如(我现在只是在编造这个),这样的库可能会模拟您可能用来手动执行大量算术的实际技术。此类库通常比使用内置算法慢得多,但有时需要额外的精度和准确度。
【讨论】:
好吧,不管怎样,当涉及到算术运算时,比如将两个大数相加,它们都必须在寄存器中,而寄存器的大小是不够的。这个问题是如何解决的? 您可以通过执行许多较小的算术运算来执行单个大型算术运算。我想我在第二段中说得很清楚...... 你不会一下子在一张纸上把 123098712380714091823 加到 120497235897345089273403 上吧?您一次执行许多较小的操作。【参考方案7】:作为一个思想实验,想象存储为字符串的数字。具有对这些任意长的数字进行加法、乘法等功能。
实际上,这些数字可能以更节省空间的方式存储。
【讨论】:
【参考方案8】:将一个机器大小的数字视为一个数字,并从小学开始应用多位乘法算法。这样您就不需要将整数保存在寄存器中,只需将数字保存在它们的处理上即可。
【讨论】:
【参考方案9】:大多数语言将它们存储为整数数组。如果您添加/减去这些大数字中的两个,则库会分别添加/减去数组中的所有整数元素并处理进位/借位。 这就像学校里的手动加/减法,因为它在内部是这样工作的。
一些语言使用真正的文本字符串而不是整数数组,这样效率较低但更容易转换为文本表示形式。
【讨论】:
以上是关于编程语言如何处理大数算术的主要内容,如果未能解决你的问题,请参考以下文章