做一个可能溢出的乘法然后用除法验证

Any problems doing a multiplication with potential overflow and then verifying with division?

本文关键字:然后 验证 除法 溢出 一个      更新时间:2023-10-16

假设我有两个size_t变量,我需要将它们相乘并得到size_t的结果。

size_t first = ...;
size_t second = ...;
size_t result = first * second;

它们可能溢出,所以我需要检查一下。

"干净"的方法是首先使用除法检查乘法是否可行:

if( second != 0 && first > ((size_t)-1) / second ) {
   //handle overflow
}
//proceed with computing first * second

看起来不那么"干净"的方法是先乘,然后用除法检查结果:

size_t result = first * second;
if( second != 0 && result / second != first )
   //handle overflow
}

然而,由于无符号数的乘法通过在零周围包装而"安全溢出",这工作得很好,看起来就像以前的代码(先检查,然后相乘)。

第二个代码有什么潜在的问题吗?它会永远和第一个一样好吗?

我想说的是,你不仅需要检查除数,还应该检查余数:

if( second != 0 && (result / second != first || (result % second) != 0))

如果c=b*a,则c只有一个值,如c/b=a。尝试用任何其他值除以b都不会得到a。

但这不是整数数学,而是真正的数学。由于整数除法取四舍五入,因此该公式在技术上不成立,因为,假设b至少为2,(c+1)/b对于c的某些值也将是a。

从一个数学POV, 如果

((a*b) mod (2^n)) / b == a  

对于某些溢出值为真(这意味着a和b都不为0),那么这是一个问题。
根据Wolfram Alpha的说法,溢出永远不会成立,因为为了得到一个结果,比特长度
结果变量n的值必须大于a*b (log[2](a*b))所需的位长。

代码方面,我现在也想不出任何问题

您上面指出的第一个方法绝对是不正确的。如上所述,将负数强制转换为无符号类型是不正确的,并且会导致未定义行为。

第二种方法,作为一个乘积可以不同于两个因子的乘法的唯一方法是在中间有一个溢出,将是正确的,但是再次,溢出可以处理未定义的行为,因此,将是不正确的(您隐含地认为溢出将由乘法模操作产生,结果确实是未定义的)。

检查可能溢出的一种方法是:

if (MAX_UINT / one_of_the_factors < the_other_factor) ...

这是完全定义的,允许您以可移植的方式在执行实际乘法之前检测溢出。