glibc`div()`代码中的Bug? 如“C++99”(即C89)和C++前的C++(即C++ 98和C++ 03)中的“P”>,对于整数运算,其中任一个操作数都是负的,其余的符号(或等价地,商的舍入方向)是定义的实现。

glibc`div()`代码中的Bug? 如“C++99”(即C89)和C++前的C++(即C++ 98和C++ 03)中的“P”>,对于整数运算,其中任一个操作数都是负的,其余的符号(或等价地,商的舍入方向)是定义的实现。,c++,c,implementation,glibc,integer-division,C++,C,Implementation,Glibc,Integer Division,然后是标准函数,该函数指定将商截断为零(即余数与被除数(分子)具有相同的符号)(参见示例) 以下是glibc为div()()(也在“”中引用)编写的代码: (注:div\u t定义为: typedef struct { int quot; int rem; } div_t; --尾注。) /*返回NUMER对DENOM的'div\t'表示形式*/ 分区 部门(数字、名称) 整数; { 分割结果; result.quot=数值/分母; result.rem=数值%deno

然后是标准函数,该函数指定将商截断为零(即余数与被除数(分子)具有相同的符号)(参见示例)

以下是glibc为
div()
()(也在“”中引用)编写的代码:

(注:
div\u t
定义为:

typedef struct
  {
    int quot;
    int rem;
  } div_t;
--尾注。)

/*返回NUMER对DENOM的'div\t'表示形式*/
分区
部门(数字、名称)
整数;
{
分割结果;
result.quot=数值/分母;
result.rem=数值%denom;
/*ANSI标准规定“QUOT”=0&&2<0)
,这是不正确的,上述函数根据需要返回
-8
-2
  • 如果
    -42/5
    产生
    -9
    ,那么
    -42%5
    产生
    3
    (正如
    -42=-9*5+3
    ),那么测试是
    (-42>=0&&3<0)
    ,这是不正确的!上述函数-9
    3
    ,而不是
    -8
    -2
  • 上面代码中的注释首先似乎是正确的,它说需要更正的情况是“REM的数字符号相反”,但随后它进行了巨大的简化“这一切归结为:如果数字>=0,但REM<0,我们得到了错误的答案”,这似乎是错误的(不完整的)因为它省略了“如果NUMER<0,但是REM>0”(
    -42
    3

    我很难相信这样一个bug自1992年或1990年以来一直没有被注意到(显然,但它似乎仍然不正确,因为
    div(-42,5)
    可能返回
    -10
    8
    )……可以说,大多数实现在默认情况下都被截断为零(从C99和C++11开始,所有人都必须这样做,所以这个问题在最新的标准1中是“没有意义的”)所以这个错误不会在他们身上出现,但仍然……也许我在这里遗漏了什么

    谢谢你的见解



    1(编辑)至于“这个问题在C++11和C99(及更新版本)中是没有意义的”:因此,在这些标准中,内置除法需要向零截断,因此我们永远不需要调整结果,但这是否意味着当前的实现比需要的更复杂,并且不必要地效率低下是过时的,
    如果
    测试没有用,那么该部分不应该完全删除吗?

    作为代码的原始作者,我不得不说:你是对的。它已经损坏了。我们没有任何系统在测试时表现出“错误的方式”,我可能在当天写上述内容太晚了(或太早了…)

    我们被更新的标准保存了下来,整个东西都应该清理干净,如果需要的话,可以为C99之前的版本添加一个小的
    #ifdef
    (和修正的调整代码)


    (我还将注意到,原始版本没有使用GNU样式的缩进进行缩进:-))

    带符号操作数的模运算符依赖于实现。我认为这是这个问题的主要症结所在。@Jim
    div
    的目的正是封装依赖于实现的操作,以给出独立于实现的结果。@ouah您的链接与我问题中的最后一个链接()正如我所说,对于
    num==-42
    denom==5
    如果你得到
    r.quot=-9
    r.rem=3
    ,新的“修正”
    -r.quot和
    r.rem+=5将给出
    r.quot=-10
    r.rem==8
    (而不是
    r.quot=-8
    r.rem=-2)…我想他们是从另一个角度看问题的。他们精确地定义了div的w.r.t.%,他们承认div是依赖于实现的。(为什么要使用混合符号运算符进行模运算呢?我不认为这是一个严重的问题,所以不太关心一致性。@是的,至少它尝试处理它:)增加的修正看起来更针对
    -42/-5
    ,它“可以”给出
    9
    3
    ,而不是
    8
    -2
    (欧几里德除法)(但这几乎和想象
    42/5
    给出
    9
    -3
    而不是
    8
    2
    一样牵强(无论如何,这是标准所禁止的。)Chris Torek!就是你!非常感谢你的回答!真的我不希望有更好的来源:)(至于缩进,可能是原始的更接近这个?)(同时,我一直在做更多的网络搜索,并发现(XOR似乎合法,但调整错误),尤其是(!)它似乎能正确处理42/-5和-42/5!)是的,这基本上是原始的(K&R-ish,“高凸度”缩进:-)…最终成为FreeBSD的风格(9)。我以前从未见过Sprite版本,也没有见过Vrije Universiteit版本。xor似乎有风险(签名)
    int
    static
    方法是有效的,但在所有情况下,运行时测试的需求似乎都是杂乱无章的。即使我们不能指望C99,也最好有一个特定于编译器的定义来描述整数除法行为,因为很多机器一开始就“按照C99想要的方式运行”。事实上,我已经提交了一份文件。(顺便说一句,Vrije Universiteit one很接近,但例如,在一台总是将商朝着-无穷大铺底的机器上,
    div(-10,5)
    ,它产生
    {-2,0}
    ,将被错误地“更正”为
    {-1,-5}
    。)
    /* 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;
    }