2011-07-16 7 views
16

Istnieje funkcja nazywa div w C, C++ (stdlib.h)Czy funkcja div jest przydatna (stdlib.h)?

div_t div(int numer, int denom); 

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

Ale C, C++ ma/i% operatorów.

Moje pytanie brzmi: "Kiedy jest/i% operatorów, czy funkcja div jest użyteczna?"

+0

Odpowiedź na to pytanie została już udzielona w [to pytanie] (http://stackoverflow.com/a/11726016/995714) –

Odpowiedz

13

Funkcja div() zwraca strukturę, która zawiera iloraz i pozostałość podziału pierwszego parametru (licznika) o sekundę (mianownik). Istnieją cztery warianty:

  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 reprezentuje największy typ całkowitą dostępną w systemie)

Struktura div_t wygląda tak:

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

Implementacja używa po prostu operatorów / i %, więc nie jest to funkcja bardzo skomplikowana lub niezbędna, ale jest częścią standardu C (zdefiniowanego przez [ISO 9899: 201x] [1]).

Zobacz wdrożenie w 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: źródło jest zresztą zdefiniowane przez implementację :-) – Vlad

+0

Vlad: Zobacz moją odpowiedź dla wyjaśnienia. –

+1

Z specyfikacją C11 (i C99?) Na podział i pozostałość, czy zgodny kompilator ma nawet 'numer> = 0 && result.rem <0'? – chux

15

Tak, jest: oblicza iloraz i pozostałość w jednej operacji.

Poza tym, samo zachowanie może zostać osiągnięty z / + % (i godnej optymalizator zoptymalizować je w jeden div tak).

Aby to podsumować: jeśli zależy Ci na wyciśnięciu ostatnich bitów wydajności, może to być Twoja ulubiona funkcja, zwłaszcza jeśli optymalizator na Twojej platformie nie jest tak zaawansowany. Jest tak często w przypadku platform wbudowanych. W przeciwnym razie użyj tego, co uważasz za bardziej czytelne.

+0

Przyzwoity optymalizator zastąpi '/' i '%' dwoma mnożnikami, nieco przesunięciem i odejmowanie (w większości przypadków). –

+0

@ Vlad: Czy masz na myśli obliczanie ilorazu i pozostała część w jednej operacji jest tak ważna, że ​​zdefiniowano nową funkcję? –

+0

@Ben: są 2 mnożniki, bit shift i odejmowanie naprawdę szybsze niż jedna instrukcja 'DIV' (w rodzinie Intela)? – Vlad

2

Kosztuje mniej czasu, jeśli potrzebujesz obu wartości. Procesor zawsze oblicza resztę i iloraz przy wykonywaniu podziału. Jeśli użyjesz "/" raz i "%" raz, cpu obliczy podwójną liczbę.

(wybacz mój słaby angielski, nie jestem native)

+3

-1: To nie jest całkowicie poprawne, ponieważ kompilator i tak prawdopodobnie je zoptymalizuje. Ponadto div() musi wykonać dodatkową kontrolę w celu zagwarantowania, że ​​wynik jest nieujemny. –

+0

@ Jørgen Fogh Twierdzenie "div() musi wykonać dodatkową kontrolę" nie jest ściśle poprawne. Różne procesory zapewniają iloraz i resztę, jak określono przez 'div()' bez uciekania się do "dodatkowego sprawdzenia". Jest to problem zależny od platformy. Czek może lub może być potrzebny. – chux

0

Prawdopodobnie dlatego na wielu procesorach dyspozycja div produkuje zarówno wartości i zawsze można liczyć na kompilator rozpoznać, że sąsiednie/i% operatorów na tym samym dane wejściowe mogą zostać połączone w jedną operację.

9

semantyki div() jest inna niż semantyki% i /, co jest ważne w niektórych przypadkach. Dlatego następujący kod jest w realizacji przedstawionej w odpowiedzi na psychotycznych:

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

% może powrócić negatywną odpowiedź, podczas gdy div() zawsze zwraca nieujemną resztę.

Sprawdź WikiPedia entry, w szczególności "div zawsze zaokrągla w kierunku 0, w przeciwieństwie do zwykłego podziału liczby całkowitej w C, gdzie zaokrąglanie liczb ujemnych jest zależne od implementacji."

+0

To nie jest prawda. 1) Po obcięciu w kierunku zera, [reszta będzie ujemna, jeśli "numer <0"] (http://ideone.com/J28Gk9). 2) Ponieważ C11, wbudowane '/' i '%' są gwarantowane, że skracają się do zera, więc 'div' jest zdefiniowany tak samo jak'/'i'% '. – ybungalobill

3

div() wypełnione konieczności wstępnego C99: przenoszenie

Pre C99 zaokrąglania kierunku iloraz a/b ujemnym argumentu było zależne od implementacji. W przypadku div() kierunek zaokrąglania nie jest opcjonalny, ale podany jako w kierunku 0. div() zapewniał jednolity podział przenośny. Użyteczna okazała się potencjalna skuteczność, gdy kod jest potrzebny do obliczenia ilorazu i pozostałej wartości.

Z C99 i później div() i / określenie tej samej rundzie kierunek i lepsze kompilatory optymalizacji pobliżu a/b i a%b kod, potrzeba zmalała.


To był powód, dla div()i wyjaśnia nieobecność udiv_t udiv(unsigned numer, unsigned denom) w C specyfikacji: kwestie zależne od implementacji wyników a/b z negatywnymi argumenty są nieistniejące dla unsigned nawet w pre-C99 .