哪种方法可以在乘法上测试有符号整数溢出?

时间:2014-04-19 06:17:58

标签: c++ c language-lawyer

如果以下任何一项"正确的事情"符合标准的方式?您可以假设mn的类型为int(有符号整数)。主要问题是有符号整数溢出。

样本1。

size_t bytes = n * m;
if (n > 0 && m > 0 && SIZE_MAX/n >= m) {
    /* allocate “bytes” space */
}

样本2。

if (n > 0 && m > 0 && SIZE_MAX/n >= m) {
    size_t bytes = n * m;
    /* allocate “bytes” space */
}

样本3。

if (n > 0 && m > 0 && SIZE_MAX/n >= m) {
    size_t bytes = (size_t)n * (size_t)m;
    /* allocate “bytes” space */
}

我认为他们都错了,但并非所有人都出于同样的原因。那么什么是正确的呢?

这些摘录取自here


编辑强调主要问题是乘以有符号整数,这可能导致未定义行为(未签名的行为)。

我现在认为最后一个样本正常工作,只要整数,有符号整数,size_t和SIZE_MAX都有"通常"价值,或至少符合相关标准。

2 个答案:

答案 0 :(得分:0)

1和2是错误的,因为n * m可能会溢出。您继续将其分配给size_t的事实不会反转"溢出。

我认为3是正确的。 (m上的演员是多余的BTW)。如果有人不同意,请发布导致测试不正确的mn以及SIZE_MAX的示例值!

我也看不到问题:

if ( m > 0 && n > 0 && (size_t)m * n / n == m )
    size_t bytes = (size_t)m * n;

答案 1 :(得分:0)

示例1不正确,因为m * n的乘法可能会超出有符号整数类型的可表示值范围,从而导致未定义的行为。见n1570 S6.5 / 5:

  

如果在评估表达式期间发生异常情况(即,如果出现异常情况)   结果不是数学定义的,也不是在其可表示值的范围内   类型),行为未定义。

这具有实际意义,因为允许编译器执行的任务之一是假定未发生未定义的行为,并且它可以使用它来优化代码,以便if语句消失并且是没有效果。

请注意,这仅适用于签名类型。无符号算术不会溢出。见n1570 S6.2.5 / 9.

  

涉及无符号操作数的计算永远不会溢出,   因为无法用结果无符号整数类型表示的结果是   减少模数可以是最大值的数字   由结果类型表示。

示例2不正确,因为m * n的乘法被计算为有符号整数表达式,这可能再次导致未定义的行为。然而,在这种情况下,实际效果的可能性较小。结果很可能产生正确的逐位结果,一旦转换为无符号,该值可能是正确的。样本是错误的,但更好,至少对于32位处理器。

编辑:如果unsigned intsize_t的大小相同,则上述参数将为真,因为它们通常用于32位处理器。在size_t大于unsigned int的情况下,样本2将无法正常工作,因为它通常位于64位处理器上。带符号的乘法将溢出并为某些值产生错误的行为。

样本3几乎肯定是正确的。将m * n的乘法计算为无符号整数表达式,不存在未定义行为的可能性。 [如另一个答案所述,实际上只需要一次施法操作。]

此处的确切行为巧妙地取决于intunsigned intsize_tSIZE_MAX的定义。通常的假设是size_tunsigned int(或者更大)相同,而SIZE_MAX是无符号整数文字,但这些实际上并不是要求。实际上,SIZE_MAX只需要至少65535。至少size_t必须是未签名的。

由于我无法找到导致样本失败的任何合理(甚至难以置信)的定义,在此基础上我说样本3正常工作。