没有进位标志的大整数加法
big integer addition without carry flag
在汇编语言中,通常有一个指令添加两个操作数和一个进位。如果要实现大整数加法,只需添加不带进位的最低整数和带有进位的下一个整数。如何在无法访问携带标志的 C 或 C++ 中有效地做到这一点?它应该适用于多个编译器和体系结构,所以我不能简单地使用内联汇编等。
"钉子"(GMP中的术语):在表示数字时,不要使用uint64_t
的所有64位,而只使用其中的63位,顶部位为零。这样,您可以通过简单的位移来检测溢出。你甚至可能想要少于63。
或者,你可以做半字算术。如果您可以进行 64 位算术运算,请将您的数字表示为 uint32_t
s 数组(或者等效地,将 64 位字拆分为上部和下部 32 位块)。然后,当对这些 32 位整数进行算术运算时,您可以先提升到 64 位在那里进行算术运算,然后转换回来。这可以让您检测进位,如果您没有"乘法 hi"指令,它也有利于乘法。
如另一个答案所示,您可以通过以下方式检测无符号添加中的溢出:
uint64_t sum = a + b;
uint64_t carry = sum < a;
顺便说一句,虽然在实践中这也适用于有符号算术,但您有两个问题:
- 它更复杂
- 从技术上讲,溢出有符号整数是未定义的行为
所以你通常最好坚持使用无符号的数字。
您可以计算出进位,因为如果您通过添加两个数字来溢出,则结果将始终小于其他两个值中的任何一个。
换句话说,如果a + b
小于a
,它就会溢出。当然,这是针对a
和b
的正值,但您几乎肯定会将其用于bignum库。
不幸的是,进位引入了额外的复杂性,因为添加最大可能的值加上一个进位将给你与开始时相同的价值。因此,您必须将其作为特例处理。
像这样:
carry = 0
for i = 7 to 0:
if a[i] > b[i]:
small = b[i], large = a[i]
else:
small = a[i], large = b[i]
if carry is 1 and large is maxvalue:
c[i] = small
carry = 1
else:
c[i] = large + small + carry
if c[i] < large:
carry = 1
else
carry = 0
实际上,您可能还需要考虑不使用数组元素中的所有位。
我过去曾实现过库,其中最大"数字"小于或等于它可以容纳的最高值的平方根。因此,对于 8 位(八位字节)数字,您可以存储从 0 到 15 的值 - 这样,将两位数相乘并添加最大进位将始终适合八位字节,使溢出检测变得毫无意义,尽管代价是一些存储。
同样,16 位数字的范围为 0 到 255,因此它不会在 65536 处溢出。
事实上,我有时会将其限制为更多,确保人工包装值是 10 的幂(因此八位字节将容纳 0 到 9,16 位数字将容纳 0 到 99,32 位数字从 0 到 9999,依此类推。
这在空间上有点浪费,但使文本之间的转换(例如打印您的数字)非常容易。
您可以通过检查结果小于操作数(任何操作数都可以)来检查无符号类型的携带。
只需以 0 开始。
理解正确,你想为你自己的大整数类型写你自己的加法。
您可以使用一个简单的函数来做到这一点。无需担心第一次运行的携带标志。只需从右到左,逐个数字和进位标志(在该函数内部)添加,从进位 0 开始,并将结果设置为 (a+b+carry) %10,将进位设置为 (a+b+carry)/10。
此 SO 可能是相关的:如何在 C 语言中实现 big int
- 如何打印boost多精度128位无符号整数
- 在不添加进位的情况下添加数字的C++程序
- 如何将整数向量转换为 2 位十六进制并按顺序添加到 c 字符串?
- 溢出事件中的进位
- 我可以将指针地址(即十六进制整数)转换为C 中的十进制和八分位基础
- 将浮点数 32 位变量类型转换为无符号整数 32 位时进行了哪些位级更改
- 没有进位标志的大整数加法
- C++将升压多精度整数转换为 64 位长度十六进制
- GCC 随进位旋转
- 如何从无符号字符(8位)缓冲区读取短整数(16位)
- 无法通过进位使价值传播
- 在我的 7 位加法器中,进位不正确,我不知道为什么
- 检测32位双字+双字进位/C++
- 32位和64位无符号整数的位移位
- 如何对两个大整数进行位与运算
- 左移一个整数32位
- 改变一个整数的位
- 有效的128位加法使用进位标志
- 负8位整数的Qt十六进制表示
- 获取64位整数中位位置的数组