glibc`div()`代码中的错误?
如C99之前的C(即C89)和C ++ 11之前的C ++(即C ++ 98和C ++ 03)中的C中的“整数除法与C ++中的负数四舍五入”所引用的那样,或者操作数是负的,其余的符号(或者等价地,商的舍入方向)是实现定义的。
然后是标准函数std::div
,它被指定为将商截短为零(即,余数与分数(分子)具有相同的符号)(例如,参见“div()库函数的用途?“)。
这里是glibc的代码为div()
(源)(也引用“是div函数有用(stdlib.h)?”):
(注意: div_t
被定义为:
typedef struct
{
int quot;
int rem;
} div_t;
- 结束注释。)
/* Return the `div_t' representation of NUMER over DENOM. */
div_t
div (numer, denom)
int numer, denom;
{
div_t result;
result.quot = numer / denom;
result.rem = numer % denom;
/* The ANSI standard says that |QUOT| <= |NUMER / DENOM|, where
NUMER / DENOM is to be computed in infinite precision. In
other words, we should always truncate the quotient towards
zero, never -infinity. Machine division and remainer may
work either way when one or both of NUMER or DENOM is
negative. If only one is negative and QUOT has been
truncated towards -infinity, REM will have the same sign as
DENOM and the opposite sign of NUMER; if both are negative
and QUOT has been truncated towards -infinity, REM will be
positive (will have the opposite sign of NUMER). These are
considered `wrong'. If both are NUM and DENOM are positive,
RESULT will always be positive. This all boils down to: if
NUMER >= 0, but REM < 0, we got the wrong answer. In that
case, to get the right answer, add 1 to QUOT and subtract
DENOM from REM. */
if (numer >= 0 && result.rem < 0)
{
++result.quot;
result.rem -= denom;
}
return result;
}
正如你所看到的,在大注释块之后有一个测试,其目的在于如果内置分割朝向-infinity而不是朝向零截断则“校正”结果。
现在的问题是:
该代码中没有错误?
我们先来看看例子调用div(42, -5)
。 “在数学中” 42 / -5正好是-8.4 ,因此在理论上C89和C ++ 03 42 / -5
可能会根据实现产生-8
(截断)或-9
(floored)。 阅读代码:
42 / -5
产生-8
那么42 % -5
产生2
(如42 == -8 * -5 + 2
),所以测试是(42 >= 0 && 2 < 0)
,这是不正确的并且上述函数根据需要返回-8
和2
; 42 / -5
产生-9
那么42 % -5
产生-3
(如42 == -9 * -5 + -3
),所以测试是(42 >= 0 && -3 < 0)
所以上述函数返回所需的“校正” -9 + 1
和-3 - -5
,即-8
和2
。 但现在让我们考虑一下调用div(-42, 5)
(符号倒置):
-42 / 5
产生-8
则-42 % 5
产生-2
(如-42 == -8 * 5 + -2
),所以测试是(-42 >= 0 && -2 < 0)
如上所示,true和上述函数返回-8
和-2
; -42 / 5
产生-9
则-42 % 5
产生3
(因为-42 == -9 * 5 + 3
),所以测试是(-42 >= 0 && 3 < 0)
真正! 并且上述函数返回-9
和3
而不是-8
和-2
! 上面的代码中的评论首先看起来是正确的,当它说需要更正的情况是当“REM与NUMER有相反的符号”时,但是这会使得很大的简化“这一切归结为:如果NUMER> = 0 ,但是REM <0,我们得到了错误的答案“,这对我来说似乎是错误的(不完整的),因为它省略了 ”如果NUMER <0,但REM> 0“(在前面的例子中为-42
和3
)的情况。
我很难相信自1992年或1990年以来,这样一个bug一直未被人注意(显然有人试图“修复”它,但它似乎仍然不正确,因为div(-42, 5)
可能会返回-10
和8
)......可以说,大多数实现在默认情况下已经被截断为零(并且从C99和C ++ 11开始都需要这样做,所以这个问题在最新的标准1中是“没有意义的”),所以错误不会显示在它们上面,但是仍然...也许我在这里失去了一些东西?
感谢您的任何见解。
1(编辑)至于“这个问题在C ++ 11和C99(和更新版本)中是没有意义的”:因此,在这些标准中,内置除法需要截断为零,所以我们永远不需要调整结果,但是这并不意味着目前的实施比需求更为复杂且不必要的低效? “大评论”已经过时, if
测试没用,那么不应该完全删除那部分?
作为代码的原始作者,我不得不说:你是对的。 它坏了。 我们没有任何系统在测试中表现出“错误的方式”,并且我可能在一天或更晚的时候写了上面的内容(或早或晚)。
我们被更新的标准保存,整个事情应该被清理掉,如果需要的话,可以用一个小的#ifdef
(和校正后的调整代码)用于C99之前的版本。
(另外我会注意到原始文件并没有用缩进格式缩进:-))
但是在这种情况下,numer等于-42,(因此它是负的)这使得条件自动失效。 事实上,如果你试图执行一个简单的包含函数的代码,你会看到(调试它)if语句:
// false in this case, numer = -42 and result.rem = -2
if(numer >= 0 && result.rem < 0)
在这里你可以看到编译结果:在这里输入链接描述
我希望答案是正确的,并会帮助你理解错误。
链接地址: http://www.djcxy.com/p/72965.html上一篇: Bug in glibc `div()` code?
下一篇: jQuery AJAX or XHR request trigger fail callbacks from done callback