it-swarm.com.de

Schnelle Obergrenze einer ganzzahligen Division in C/C++

Bei ganzzahligen Werten x und y geben C und C++ beide als Quotienten q = x/y die Untergrenze des Gleitkommaäquivalents zurück. Ich interessiere mich stattdessen für eine Methode zur Rückgabe der Decke. Zum Beispiel ceil(10/5)=2 und ceil(11/5)=3.

Der naheliegende Ansatz beinhaltet etwas wie:

q = x / y;
if (q * y < x) ++q;

Dies erfordert einen zusätzlichen Vergleich und eine Multiplikation. und andere Methoden, die ich gesehen habe (in der Tat verwendet), beinhalten das Casting als float oder double. Gibt es eine direktere Methode, die die zusätzliche Multiplikation (oder eine zweite Division) und die Verzweigung vermeidet und auch das Casting als Fließkommazahl vermeidet?

207
andand

Aufrunden ...

q = (x + y - 1) / y;

oder (Überlauf in x + y vermeiden)

q = 1 + ((x - 1) / y); // if x != 0
327
Sparky

Für positive Zahlen:

    q = x/y + (x % y != 0);
59

Die Antwort von Sparky ist eine Standardmethode, um dieses Problem zu lösen, aber wie ich auch in meinem Kommentar schrieb, laufen Sie Gefahr, dass Überläufe entstehen. Dies kann durch Verwendung eines breiteren Typs gelöst werden. Was ist, wenn Sie long longs teilen möchten?

Die Antwort von Nathan Ernst bietet eine Lösung, beinhaltet jedoch einen Funktionsaufruf, eine Variablendeklaration und eine Bedingung, die ihn nicht kürzer als der OPs-Code macht und wahrscheinlich sogar langsamer ist, da die Optimierung schwieriger ist.

Meine Lösung ist folgende:

q = (x % y) ? x / y + 1 : x / y;

Es wird etwas schneller als der OPs-Code sein, da das Modulo und die Division mit derselben Anweisung auf dem Prozessor ausgeführt werden, da der Compiler erkennen kann, dass sie gleichwertig sind. Zumindest gcc 4.4.1 führt diese Optimierung mit -O2-Flag auf x86 durch.

Theoretisch könnte der Compiler den Funktionsaufruf in Nathan Ernsts Code integrieren und dasselbe ausgeben, aber gcc hat das nicht getan, als ich ihn getestet habe. Dies kann daran liegen, dass der kompilierte Code an eine einzelne Version der Standardbibliothek gebunden wird.

Als letzte Bemerkung ist auf einem modernen Computer nichts davon von Bedeutung, es sei denn, Sie befinden sich in einer extrem engen Schleife und alle Ihre Daten befinden sich in Registern oder im L1-Cache. Ansonsten sind alle diese Lösungen gleich schnell, mit Ausnahme der möglicherweise von Nathan Ernst, die möglicherweise erheblich langsamer ist, wenn die Funktion aus dem Hauptspeicher abgerufen werden muss.

56
Jørgen Fogh

Sie können die Funktion div in cstdlib verwenden, um den Quotienten und den Rest in einem einzigen Aufruf abzurufen und die Decke dann separat zu behandeln (siehe unten)

#include <cstdlib>
#include <iostream>

int div_ceil(int numerator, int denominator)
{
        std::div_t res = std::div(numerator, denominator);
        return res.rem ? (res.quot + 1) : res.quot;
}

int main(int, const char**)
{
        std::cout << "10 / 5 = " << div_ceil(10, 5) << std::endl;
        std::cout << "11 / 5 = " << div_ceil(11, 5) << std::endl;

        return 0;
}
16
Nathan Ernst

Wie wäre es damit? (erfordert y nicht negativ, verwenden Sie dies also nicht in den seltenen Fällen, in denen y eine Variable ohne Garantie für Nicht-Negativität ist.)

q = (x > 0)? 1 + (x - 1)/y: (x / y);

Ich habe y/y auf eins reduziert, den Begriff x + y - 1 und damit die Möglichkeit eines Überlaufs eliminieren.

Ich vermeide, dass x - 1 umfließt, wenn x ein vorzeichenloser Typ ist und Null enthält.

Bei x mit Vorzeichen vereinigen sich Negativ und Null immer noch zu einem einzelnen Fall.

Wahrscheinlich kein großer Vorteil bei einer modernen Universal-CPU, aber dies wäre bei einem Embedded-System weitaus schneller als jede der anderen richtigen Antworten.

12
Ben Voigt

Dies funktioniert für positive oder negative Zahlen:

q = x / y + ((x % y != 0) ? !((x > 0) ^ (y > 0)) : 0);

Wenn ein Rest vorhanden ist, prüfen Sie, ob x und y dasselbe Vorzeichen haben, und fügen dementsprechend 1 Hinzu.

5
Mark Conway

Es gibt eine Lösung für positive und negative x, jedoch nur für positive y mit nur einer Division und ohne Verzweigungen:

int ceil(int x, int y) {
    return x / y + (x % y > 0);
}

Wenn x positiv ist, ist die Division gegen null und wir sollten 1 hinzufügen, wenn die Erinnerung nicht Null ist.

Wenn x negativ ist, ist die Division gegen Null, das ist es, was wir brauchen, und wir werden nichts hinzufügen, da x % y nicht positiv ist

5
RiaD

vereinfachte generische Form, 

int div_up(int n, int d) {
    return n / d + (((n < 0) ^ (d > 0)) && (n % d));
} //i.e. +1 iff (not exact int && positive result)

Für eine allgemeinere Antwort: C++ - Funktionen für die Ganzzahldivision mit gut definierter Rundungsstrategie

2
Atif Hussain

Ich hätte es lieber kommentiert, aber ich habe keine ausreichend hohe Wiederholung.

Soweit ich weiß, ist dies für + ve & pow von 2 der schnellste Weg (getestet in CUDA)

//example y=8
q = x >> 3 + !!(x & 7);

ansonsten (auch nur + ve) würde ich es gerne so machen:

q = x/y + !!(x % y);
0
Anroca