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