我想要将两个数字相乘,同时检测是否发生了溢出。最简单的方法是什么?
我想要将两个数字相乘,同时检测是否发生了溢出。最简单的方法是什么?
000 : 0 * 100 000 : 0 * 100 100 : 1 * 100将这些列加起来,答案为0b10000。与十进制数学中的百位上为1表示复制顶部数字并添加两个零相同,在任何其他进制中也是如此。因此,0b100乘以0b110等于0b1000,在第二列中为1,因此复制并添加一个零+0b10000,在第三列中为1,因此复制并添加两个零=0b11000。
-8 * 7 算出的结果是 4+4=8 位,实际上是 7 位。 -1 * 7 算出的结果是 1+4=5 位,实际上是 4 位。 -8 * -8 算出的结果是 4+4=8 位,实际上是 8 位。 -1 * -1 算出的结果是 1+1=2 位,实际上是 3 位。 -1 * -8 算出的结果是 1+4=5 位,实际上是 5 位。 7 * 7 算出的结果是 4+4=8 位,实际上是 7 位。
这个规则适用于大多数情况,但对于 -1 * -1 这种情况需要注意,因为我们将 -1 视为一位,对于正数可以找到零加一。不管怎样,如果按照定义一个 4 位 * 4 位的机器,至少应该有 4 位的结果,我认为题目的意思是要确定是否需要使用超过 4 位的空间来安全存储答案。因此,这个规则可以回答这个问题,适用于二进制补码运算。
如果你的问题是精确地确定溢出并且速度其次,那么某些系统可能会非常慢,每次乘法都需要这样做。如果你这样问,为了提高一些速度,需要根据语言和/或处理器优化它。如果可以,将最大的操作数加倍,检查结果大小上方是否有非零位,或使用除法和比较。如果不能将操作数大小加倍,则使用除法和比较。在除法之前检查是否为零。
实际上,你的问题也没有说明你要考虑哪种类型的溢出。老式的 8086 16 位乘以 16 位的硬件可以得到 32 位的结果,永远不会溢出。那么像一些 ARM 处理器的乘法,32 位乘以 32 位的结果很容易溢出。对于这个问题,操作数的大小是多少?它们是相同大小还是双倍输入大小?您愿意执行硬件无法完成(而不会溢出)的乘法吗?如果您正在编写编译器库,并尝试确定是否可以将操作数馈送到硬件以获得速度,还是必须执行不带硬件乘法的计算。这是您转换操作数后的情况,编译器库将尝试在执行乘法之前将操作数转换回来,这取决于编译器及其库,它将使用统计位数的技巧来确定使用硬件乘法还是软件乘法。我的目标是用一种易于理解的形式展示二进制乘法如何工作,以便您可以通过查找每个操作数中单个位的位置来确定需要多少最大存储空间。现在,您能够快速找到每个操作数中的那个位就是诀窍。如果您正在寻找最小存储要求而不是最大存储要求,那么情况就不同了,因为它涉及到两个操作数中所有显著位,而不仅仅是一个操作数中的一位。您必须进行乘法运算才能确定最小存储需求。如果您不关心最大或最小存储,则必须执行乘法并查找超出定义的溢出限制的非零值,或者使用除法(如果您有时间或硬件)。
您的标记表明您不对浮点数感兴趣,浮点数是完全不同的东西,您无法将任何这些定点规则应用于浮点数,它们不起作用。
100_2 * 100_2 = 10000_2
不会导致一个5位值的溢出,但是 111_2 * 111_2 = 110001_2
会导致溢出。 - Christian Semraux*(2**n-x)>2**M
示例是从哪里来的。 - Christian Semrau1
,否则返回0
。int is_undefined_mult1(int a, int b) {
if (a > 0) {
if (b > 0) {
return a > INT_MAX / b; // a positive, b positive
}
return b < INT_MIN / a; // a positive, b not positive
}
if (b > 0) {
return a < INT_MIN / b; // a not positive, b positive
}
return a != 0 && b < INT_MAX / a; // a not positive, b not positive
}
如果您的数字不是最大整数数据类型,则可以将它们转换为该类型,进行乘法运算并与数字原始类型的最大值进行比较。例如,在Java中,当乘以两个int
时,您可以将它们转换为long
,并将结果与Integer.MAX_VALUE
或Integer.MIN_VALUE
(取决于符号组合)进行比较,然后将结果转换回int
。
如果类型已经是最大的,则检查一个数字是否小于另一个数字的最大值除以它自己。但不要取绝对值!相反,您需要为每个符号组合negneg、pospos和posneg(显然可以将negpos缩减为posneg,而pospos可能会缩减为neg*neg)编写单独的比较逻辑。首先测试0个参数以允许安全分割。
如需实际代码,请参见commons-math 2的MathUtils
类或ArithmeticUtils
of commons-math 3的Java源代码。查找public static long mulAndCheck(long a, long b)
。正数a和b的情况为:
// check for positive overflow with positive a, positive b
if (a <= Long.MAX_VALUE / b) {
ret = a * b;
} else {
throw new ArithmeticException(msg);
}
b != 0
后,(a * b) / b == a
)是否足够? - jamesdlina=Long.MIN_VALUE, b=-1L
,它并不起作用。 - Christian Semrau如果您选择的语言是汇编语言,那么您应该能够检查溢出标志。如果不行,您可以编写一个自定义的汇编程序,如果设置了溢出标志,则设置一个变量。
如果这不可接受,您可以找到两个值(绝对值)的最高有效位。如果它们相乘后的和超过整数(或无符号整数)的位数,则会发生溢出。
希望这可以帮助到您。
100_2 * 100_2 = 10000_2
不会溢出5位值,但是111_2 * 111_2 = 110001_2
会溢出。 - Christian Semrauint
would_mul_exceed_int(int a, int b) {
int product_bits;
if (a == 0 || b == 0 || a == 1 || b == 1) return (0); /* always okay */
if (a == INT_MIN || b == INT_MIN) return (1); /* always underflow */
a = ABS(a);
b = ABS(b);
product_bits = significant_bits_uint((unsigned)a);
product_bits += significant_bits_uint((unsigned)b);
if (product_bits == BITS(int)) { /* cases where the more expensive test is required */
return (a > INT_MAX / b); /* remember that IDIV and similar are very slow (dozens - hundreds of cycles) compared to bit shifts, adds */
}
return (product_bits > BITS(int));
}
以上方法的好处是不需要将类型转换为更大的类型,因此该方法可以适用于更大的整数类型。
a=2, b=-INT_MIN/2
。 - Christian Semrau