有一些想法在起作用。
首先,将 2 个较短的整数相乘以产生较长的乘积。考虑 2 个 32 位整数的无符号乘法,通过它们的 16 位“一半”相乘,每个乘积产生一个 32 位乘积,总乘积为 64 位:
a * b = (a_hi * 216 + a_lo) * (b_hi * 216 + b_lo) =
a_hi * b_hi * 232 + (a_hi * b_lo + a_lo * b_hi) * 216 + a_lo * b_lo。
现在,如果你需要一个有符号乘法,你可以从无符号乘法中构造它(例如从上面)。
假设a = 0,a *signed b 必须相等
264 - ((-a) *unsigned b),其中
-a = 232 - a(因为这是 2 的补码)
IOW,
a *签名 b =
264 - ((232 - a) *unsigned b) =
264 + (a *unsigned b) - (b * 232),其中 264 可以丢弃,因为我们只使用 64 位。
以完全相同的方式,您可以计算 a >= 0 和 b signed b 并且必须得到对称结果:
(a *unsigned b) - (a * 232)
您可以类似地证明,对于 a
(a *unsigned b) - ((a + b) * 232)
因此,首先将 a 和 b 相乘为无符号,然后如果 a
现在我们可以将 32 位有符号整数相乘并得到 64 位有符号乘积,我们终于可以转向小数部分了。
现在假设在 a 和 b 的这 32 位中,N 位用于小数部分。这意味着如果您将 a 和 b 视为普通整数,它们将比它们实际表示的值大 2N 倍,例如1.0 看起来像 2N(或 1
因此,如果将两个这样的整数相乘,则乘积将是 2N*2N = 22*N 倍比它应该代表的,例如1.0 * 1.0 看起来像 22*N (或 1 N (或将其在算术上向右移动 N 个位置)。很简单。
谨言慎行,以防万一……
在 C(和 C++)中,您不能合法地将变量向左或向右移动与变量中包含的相同或更多位数。代码会编译,但不会像您期望的那样工作。所以,如果你想移动一个 32 位变量,你可以将它向左或向右移动 0 到 31 个位置(31 是最大值,而不是 32)。
如果将有符号整数左移,则不能合法地溢出结果。所有带符号的溢出都会导致未定义的行为。所以,你可能想坚持使用 unsigned。
负符号整数的右移是特定于实现的。它们可以进行算术移位或逻辑移位。哪一个,这取决于编译器。因此,如果您需要两者之一,则需要确保您的编译器直接支持它
或以其他方式实现。