div函数是否有用(stdlib.h)?

19

在C、C++语言中有一个名为div的函数(stdlib.h)。

div_t div(int numer, int denom);

typedef struct _div_t
{
  int quot;
  int rem;
} div_t;

但是C、C++有 / 和 % 运算符。

我的问题是:“当有 / 和 % 运算符时,div 函数是否有用?”


它已经在这个问题中得到了回答。 - phuclv
4
可能是div()库函数的目的是什么?的重复。请注意,对于非特拉法尔马多人来说,线性时间并不重要。那个问题得到了比这个问题更好、更现代化的回答。 - underscore_d
它的答案过于关注一个细节,而这个细节在20年前就已经不再重要了,这是C99时代的事情。 - underscore_d
6个回答

18

是的,它可以:它在一次操作中计算商和余数。

除此之外,也可以通过使用/%来实现相同的行为(一个良好的优化器会将它们优化为单个div)。

总之,如果你关心挤出最后一点性能,这可能是你首选的函数,特别是如果你的平台上的优化器不那么先进。这通常适用于嵌入式平台。否则,使用你认为更易读的方式。


1
一个不错的优化器会用两个乘法、一次位移和一次减法(在大多数情况下)来取代 /% - Ben Voigt
10
历史上,CPU 分割操作非常耗费资源,因此编译器提供的任何帮助都是受欢迎的。现在,这已经不再是一个大问题,但没有理由从 C 中 移除 div() 函数,因此它仍然存在。 - Jonathan Grynspan
2
乘法技巧只在分母为常数时有效。 - R.. GitHub STOP HELPING ICE
1
@Vlad:那个部分不是建议用乘法代替除法(除以一个常数)吗?(而且128位的除法非常罕见) - Ben Voigt
@LưuVĩnhPhúc:所以你那非常昂贵的除法可以用同样的价格得到两个输出。乘法、移位和减法的组合仍然经常更便宜。 - Ben Voigt
显示剩余9条评论

14

div() 函数返回一个结构体,其中包含第一个参数(被除数)除以第二个参数(除数)的商和余数。这个函数有四种变体:

  1. div_t div(int, int)
  2. ldiv_t ldiv(long, long)
  3. lldiv_t lldiv(long long, long long)
  4. imaxdiv_t imaxdiv(intmax_t, intmax_t(intmax_t代表系统中可用的最大整数类型)

div_t 结构体的定义如下:

typedef struct
  {
    int quot;           /* Quotient.  */
    int rem;            /* Remainder.  */
  } div_t;

实现仅使用 /% 操作符,因此它不是非常复杂或必要的功能,但它是 C 标准的一部分(由 [ISO 9899:201x][1] 定义)。

请查看 GNU libc 中的实现:

/* 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;
}

1
@Jørgen:源代码的实现方式是不确定的 :-) - Vlad
Vlad:请看我的回答以获得澄清。 - Jørgen Fogh
1
使用C11(和C99?)规范进行除法和余数运算,符合规范的编译器是否会出现“numer >= 0 && result.rem < 0”的情况? - chux - Reinstate Monica
很棒的答案。这只是表明,如果标准库有一个看起来“不必要”的函数,很有可能有人写它有很好的理由。 - AAT
1
更明确地说,C11将div指定为“在单个操作中计算numer / denom和numer%denom”。自C99以来,不再需要if(numer> = 0 && result.rem <0)块。 - chux - Reinstate Monica

10

div()的语义与%和/的语义不同,在某些情况下这很重要。这就是为什么在psYchotic的答案中展示了以下代码的实现方式:

if (numer >= 0 && result.rem < 0)
    {
      ++result.quot;
      result.rem -= denom;
    }

% 可能返回一个负数,而div()总是返回一个非负余数。

请查看维基百科条目,特别是“div始终向0舍入,不像C语言中普通的整数除法,其中对于负数的舍入取决于具体实现。”


2
这是不正确的。1)当你向零截断时,如果numer < 0,则余数为负数。2)自C11以来,内置的/保证向零截断,因此div被定义为与/相同。 - Yakov Galka
同意@ybungalobill的观点。"whereas div() always returns a non-negative remainder."是不正确的。尝试printf("%d\n", div(-7,2).rem);,我预计代码将返回-1 - chux - Reinstate Monica

7

div()填补了C99之前的需求:可移植性

在C99之前,使用负数操作数计算a/b的商的舍入方向依赖于实现。使用div(),舍入方向不是可选的,而是被指定为向0舍入。 div()提供了统一的可移植除法。一个次要的用途是当代码需要同时计算商和余数时,可以提高效率。

随着C99及以后版本的到来,div()/指定相同的舍入方向,并且更好的编译器优化了附近的a/ba%b代码,因此需求已经减少。


这就是为什么需要使用div()的重要原因,也解释了C规范中缺少udiv_t udiv(unsigned numer, unsigned denom)的原因:在使用unsigned时,即使在C99之前,使用负操作数进行a/b运算所产生的实现相关结果问题是不存在的。请注意保留HTML标签。

1

可能是因为在许多处理器上,div指令会产生两个值,并且您可以始终依靠编译器识别相邻的/和%运算符在相同输入上可以合并为一个操作。


0

如果您需要两个值,使用除法运算符和取模运算符可以节省时间。 当执行除法运算时,CPU总是同时计算余数和商。 如果使用一次“/”和一次“%”,CPU将会对这两个数字进行两次计算。

(请原谅我的英语不好,我不是以英语为母语的人)


4
这并不是严格正确的,因为编译器很可能会对其进行优化。此外,div()函数还必须执行额外的检查以确保结果非负。 - Jørgen Fogh
1
@Jørgen Fogh,“div()必须执行额外的检查”这个断言并不严格正确。各种处理器根据div()指定的方式提供商和余数,而无需诉诸“额外的检查”。这是一个平台相关的问题。可能需要或不需要检查。 - chux - Reinstate Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接