2011-07-16 1 views
16

Существует функция называется DIV в C, C++ (stdlib.h)Функция div полезна (stdlib.h)?

div_t div(int numer, int denom); 

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

Но C, C++ есть/и% операторов.

Мой вопрос: «Когда есть/и% операторов, функция div полезна

+0

Об этом уже было сказано в [этот вопрос] (http://stackoverflow.com/a/11726016/995714) –

ответ

13

Функция 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

+0

Влад: См. Мой ответ для разъяснения. –

+1

С спецификацией C11 (и C99?) На деление и остаток, совместимый компилятор даже имеет 'число> = 0 && result.rem <0'? – chux

15

Да, это: он вычисляет коэффициент и остаток в один операция.

Помимо этого, такое же поведение может быть достигнуто с / + % (и приличный оптимизатор оптимизировать их в единый div в любом случае).

Чтобы подвести итог: если вы заботитесь о выдаче последних бит производительности, это может быть ваша функция выбора, особенно если оптимизатор на вашей платформе не настолько продвинут. Это часто бывает для встроенных платформ. В противном случае используйте любой способ, который вы найдете более читаемым.

+0

Приятный оптимизатор заменит '/' и '%' двумя умножениями, сдвигом бит и вычитание (в большинстве случаев). –

+0

@ Vlad: Вы имеете в виду вычисление частного и оставшегося за одну операцию настолько важно, что определена новая функция? –

+0

@Ben: 2 умножения, сдвиг бит и вычитание действительно быстрее, чем одна инструкция 'DIV' (в семействе Intel)? – Vlad

2

Это стоит меньше времени, если вам нужно оба значения. Процессор всегда вычисляет как остаток, так и коэффициент при выполнении деления. Если вы используете «/» один раз и «%» один раз, процессор будет вычислять два числа.

(простите мой плохой английский, я не родной)

+3

-1: Это не совсем правильно, поскольку компилятор, вероятно, оптимизирует его. Кроме того, div() должен выполнить дополнительную проверку, чтобы гарантировать, что результат неотрицателен. –

+0

@ Jørgen Fogh Утверждение «div() должно выполнить дополнительную проверку» не является строго правильным. Различные процессоры предоставляют коэффициент и остаток, как указано 'div()', не прибегая к «дополнительной проверке». Это проблема, зависящая от платформы. Проверка может потребоваться или может потребоваться. – chux

0

Вероятно потому, что на многих процессорах команда ДИВ производит оба значения, и вы всегда можете рассчитывать на компилятор признать, что смежные/и% операторов на том же входы могут быть объединены в одну операцию.

9

семантике DIV() отличается от семантики% и /, что важно в некоторых случаях. Именно поэтому следующий код находится в реализации, показанной в ответ психотика:

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

% может вернуться отрицательный ответ, в то время как DIV() всегда возвращает неотрицательное остаток.

Проверьте WikiPedia entry, в частности «div всегда округляется до 0, в отличие от обычного целочисленного деления на C, где округление для отрицательных чисел зависит от реализации».

+0

Это неправда. 1) Когда вы усекаетесь к нулю, тогда [остаток будет отрицательным, если 'number <0'] (http://ideone.com/J28Gk9). 2) Поскольку C11, встроенные '/' и '%' гарантированно усекаются к нулю, поэтому 'div' определяется так же, как'/'и'% '. – ybungalobill

3

div() заполнил необходимость предварительного C99: портативность

Pre C99, скругление направления частного a/b с отрицательным операндом была зависят от реализации. С div() направление закругления не является необязательным, но указывает на 0. 0. div() обеспечивает равномерное переносное разделение. A вторичный использование было потенциальной эффективностью, когда код необходим для вычисления как частного, так и остаточного.

С C99, а затем, div() и / с указанием же круглое направление и лучшие компиляторы оптимизации близлежащий a/b и a%b код, потребность уменьшилась.


Это было веской причиной для div()и это объясняет отсутствие udiv_t udiv(unsigned numer, unsigned denom) в C спецификации: вопросы реализации зависимых результатов a/b с отрицательными операндами являются несуществующими для unsigned даже в пре-C99 ,