C ++: Aufrunden auf das nächste Vielfache einer Zahl

168

OK - es ist mir fast peinlich, dies hier zu posten (und ich werde es löschen, wenn jemand zum Schließen stimmt), da es wie eine grundlegende Frage erscheint.

Ist dies der richtige Weg, um in C ++ auf ein Vielfaches einer Zahl aufzurunden?

Ich weiß, dass es andere Fragen dazu gibt, aber ich bin besonders daran interessiert zu wissen, wie dies in C ++ am besten funktioniert:

int roundUp(int numToRound, int multiple)
{
 if(multiple == 0)
 {
  return numToRound;
 }

 int roundDown = ( (int) (numToRound) / multiple) * multiple;
 int roundUp = roundDown + multiple; 
 int roundCalc = roundUp;
 return (roundCalc);
}

Update: Entschuldigung, ich habe die Absicht wahrscheinlich nicht klargestellt. Hier sind einige Beispiele:

roundUp(7, 100)
//return 100

roundUp(117, 100)
//return 200

roundUp(477, 100)
//return 500

roundUp(1077, 100)
//return 1100

roundUp(52, 20)
//return 60

roundUp(74, 30)
//return 90
Robben_Ford_Fan_boy
quelle
3
Sie haben einen Fehler in Ihrer Logik - sagen wir, ich möchte 4 auf das nächste Vielfache von 2 aufrunden. RoundDown = (4/2) * 2 = 4; roundUp = 4 + 2; also roundCalc = 6. Ich gehe davon aus, dass Sie in diesem Fall 4 zurückgeben möchten.
Niki Yoshiuchi
Dies funktioniert nicht für roundUp (30,30). Es gibt 60 als Antwort, es sollte immer noch 30 als Antwort geben ..
bsobaid
@bsobaid: Schau dir meine Antwort unten an. Es ist etwas einfacher als andere Lösungen hier, obwohl diese auch funktionieren sollten
Niklas B.
3
In Ihren Testfällen fehlen auffällig Beispiele für negative Zahlen, Fälle, in denen die Division genau ist, Fälle, in denen die Division nahezu genau ist, und Fälle, in denen die Zahlen sehr nahe an den Grenzen des Bereichs von liegen int.
1
Robben_Ford_Fan_boy, Die Bearbeitung mit der Antwort, für die Sie sich entschieden haben, sollte entfernt werden. Wenn es von den gegebenen Antworten abweicht, können Sie Ihre eigene Antwort posten. Derzeit weist diese Antwort Probleme auf, die im Antwortabschnitt behandelt werden sollten.
chux

Antworten:

161

Dies funktioniert für positive Zahlen, nicht sicher über negative. Es wird nur ganzzahlige Mathematik verwendet.

int roundUp(int numToRound, int multiple)
{
    if (multiple == 0)
        return numToRound;

    int remainder = numToRound % multiple;
    if (remainder == 0)
        return numToRound;

    return numToRound + multiple - remainder;
}

Bearbeiten: Hier ist eine Version, die mit negativen Zahlen arbeitet, wenn Sie mit "up" ein Ergebnis meinen, das immer> = die Eingabe ist.

int roundUp(int numToRound, int multiple)
{
    if (multiple == 0)
        return numToRound;

    int remainder = abs(numToRound) % multiple;
    if (remainder == 0)
        return numToRound;

    if (numToRound < 0)
        return -(abs(numToRound) - remainder);
    else
        return numToRound + multiple - remainder;
}
Mark Ransom
quelle
+1 Meiner Meinung nach definitiv die schönste und am besten lesbare Lösung.
Robben_Ford_Fan_boy
1
Fügen Sie if(number<0){ multiple = multiple*(-1); }zu Beginn hinzu, um negative Zahlen in die richtige Richtung zu runden
Josh
4
@ Josh: Warum Multiplikation verwenden? if(number<0) multiple = -multipleist einfacher.
md5
Dies funktioniert nicht für roundUp (30,30). Es gibt 60 als Antwort, es sollte immer noch 30 als Antwort geben.
bsobaid
@bsobaid unmöglich. Der if (remainder == 0)Test sollte sich um diesen Fall kümmern. Es funktioniert für mich: ideone.com/Waol7B
Mark Ransom
112

Ohne Bedingungen:

int roundUp(int numToRound, int multiple) 
{
    assert(multiple);
    return ((numToRound + multiple - 1) / multiple) * multiple;
}

Dies funktioniert wie das Abrunden von Null für negative Zahlen

EDIT: Version, die auch für negative Zahlen funktioniert

int roundUp(int numToRound, int multiple) 
{
    assert(multiple);
    int isPositive = (int)(numToRound >= 0);
    return ((numToRound + isPositive * (multiple - 1)) / multiple) * multiple;
}

Tests


Wenn multipleist eine Potenz von 2 (schneller in ~ 3,7 mal http://quick-bench.com/sgPEZV9AUDqtx2uujRSa3-eTE80 )

int roundUp(int numToRound, int multiple) 
{
    assert(multiple && ((multiple & (multiple - 1)) == 0));
    return (numToRound + multiple - 1) & -multiple;
}

Tests

KindDragon
quelle
24
+1 für die Potenz von 2 Version. Sehr nützlich, da die Kosten für Multiplikationen, Divisionen oder Modulo vollständig vermieden werden.
Nikos C.
Sind Sie sicher, dass diese Algorithmen keine Voraussetzungen haben? Was ist mit negativen Zahlen? Das Verhalten scheint in Pre-C ++ 11 undefiniert zu sein .
cubuspl42
> Was ist mit negativen Zahlen? Wie beschrieben funktioniert dies für negative Zahlen wie das Abrunden von Null.
KindDragon
Ich las "Aufrunden" als Aufrunden in Richtung positive Unendlichkeit, nicht Abrunden von Null.
8
Beachten Sie, dass dies & ~(x - 1)dasselbe ist wie & -xfür die Zweierkomplementarithmetik.
Todd Lehman
39

Dies funktioniert, wenn der Faktor immer positiv ist:

int round_up(int num, int factor)
{
    return num + factor - 1 - (num - 1) % factor;
}

Bearbeiten: Dies kehrt zurück round_up(0,100)=100. In Pauls Kommentar unten finden Sie eine Lösung, die zurückkehrt round_up(0,100)=0.

xlq
quelle
1
Es scheint der kürzeste Fall zu sein, der den Fall "bereits ein Vielfaches" behandelt.
Harningt
1
Beste Lösung in Bezug auf die Anzahl der kostspieligen Operationen. Es wird nur eine einzige Division und keine Multiplikation verwendet
Niklas B.
3
round_up (0, 100) == 100 statt 0 wie in der akzeptierten Antwort
Gregory
7
Sollte es nicht sein num + factor - 1 - (num + factor - 1) % factor?
Paul
6
num - 1 - (num - 1) % factor + factorführt die gleiche Berechnung ohne das Risiko eines ganzzahligen Überlaufs durch.
24

Dies ist eine Verallgemeinerung des Problems "Wie finde ich heraus, wie viele Bytes n Bits benötigt werden?" (A: (n Bits + 7) / 8).

int RoundUp(int n, int roundTo)
{
    // fails on negative?  What does that mean?
    if (roundTo == 0) return 0;
    return ((n + roundTo - 1) / roundTo) * roundTo; // edit - fixed error
}
Sockel
quelle
1
Dies rundet nicht auf das nächste Vielfache einer Zahl auf.
aaaa bbbb
7
Ich mag diese Lösung, denn wenn roundTo eine Potenz von 2 ist, können Sie das / und * eliminieren und erhalten nur billige Operationen (x = roundTo - 1; return (n + x) & ~ x;)
Trejkaz
@ Trejkaz nein. Es sollte (x = roundTo - 1; return (n+x)&~roundTo;)wie in meiner Antwort sein
KindDragon
@KindDragon, das für mich das falsche Ergebnis liefert, aber wenn ich es korrigiere, um ~ x anstelle von ~ roundTo zu sagen, erhalte ich das erwartete Ergebnis. Auf Java 8 sowieso.
Trejkaz
@KindDragon: Die UND-Maske muss 0xFFF...000nicht 0xFFF7FFFoder so sein, also möchten Sie entweder die Komplementnegation ( -: minus) von 2 mit einer Potenz von 2 oder einen Bit-Flip auf eine weniger als eine Potenz von 2 (das Komplement invers ~: Tilde) nicht minus). Also (n+x) & ~xoder (n-roundTo+1) & -roundTo.
Peter Cordes
14
int roundUp(int numToRound, int multiple)
{
 if(multiple == 0)
 {
  return 0;
 }
 return ((numToRound - 1) / multiple + 1) * multiple;  
}

Und Sie müssen nicht mit den Bedingungen herumspielen

Doron
quelle
11

Für alle, die eine kurze und süße Antwort suchen. Das habe ich benutzt. Keine Berücksichtigung von Negativen.

n - (n % r)

Das gibt den vorherigen Faktor zurück.

(n + r) - (n % r)

Ich werde am nächsten zurückkehren. Hoffe das hilft jemandem. :) :)

Aaron-Bindung
quelle
9
float roundUp(float number, float fixedBase) {
    if (fixedBase != 0 && number != 0) {
        float sign = number > 0 ? 1 : -1;
        number *= sign;
        number /= fixedBase;
        int fixedPoint = (int) ceil(number);
        number = fixedPoint * fixedBase;
        number *= sign;
    }
    return number;
}

Dies funktioniert für jede Float-Nummer oder Basis (z. B. können Sie -4 auf 6,75 runden). Im Wesentlichen wird in einen festen Punkt konvertiert, dort gerundet und dann zurück konvertiert. Es behandelt Negative, indem es AWAY von 0 rundet. Es behandelt auch eine negative Runde auf Wert, indem die Funktion im Wesentlichen in roundDown umgewandelt wird.

Eine int-spezifische Version sieht folgendermaßen aus:

int roundUp(int number, int fixedBase) {
    if (fixedBase != 0 && number != 0) {
        int sign = number > 0 ? 1 : -1;
        int baseSign = fixedBase > 0 ? 1 : 0;
        number *= sign;
        int fixedPoint = (number + baseSign * (fixedBase - 1)) / fixedBase;
        number = fixedPoint * fixedBase;
        number *= sign;
    }
    return number;
}

Das ist mehr oder weniger die Antwort von Sockel mit der zusätzlichen Unterstützung für negative Eingaben.

Delfin
quelle
Ich habe float roundUp Code mit double getestet, es funktioniert für mich. Löst mein Problem wirklich.
Ashif
1
Was ist mit double round(double value, double multiple) { double sign = value; multiple = std::copysign(multiple, 1.0); value = std::copysign(value, 1.0); return std::copysign(multiple * std::ceil(value / multiple), sign); }Oder tauschen Sie die Decke gegen eine runde, um eine Rundung zu erhalten.
Troyseph
8

Dies ist der moderne c ++ - Ansatz, der eine Vorlagenfunktion verwendet, die für float, double, long, int und short funktioniert (jedoch nicht für long long und long double aufgrund der verwendeten double-Werte).

#include <cmath>
#include <iostream>

template<typename T>
T roundMultiple( T value, T multiple )
{
    if (multiple == 0) return value;
    return static_cast<T>(std::round(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
}

int main()
{
    std::cout << roundMultiple(39298.0, 100.0) << std::endl;
    std::cout << roundMultiple(20930.0f, 1000.0f) << std::endl;
    std::cout << roundMultiple(287399, 10) << std::endl;
}

Sie können jedoch problemlos Unterstützung für long longund long doublemit Vorlagenspezialisierung hinzufügen, wie unten gezeigt:

template<>
long double roundMultiple<long double>( long double value, long double multiple)
{
    if (multiple == 0.0l) return value;
    return std::round(value/multiple)*multiple;
}

template<>
long long roundMultiple<long long>( long long value, long long multiple)
{
    if (multiple == 0.0l) return value;
    return static_cast<long long>(std::round(static_cast<long double>(value)/static_cast<long double>(multiple))*static_cast<long double>(multiple));
}

Um Funktionen zum Aufrunden zu erstellen, verwenden Sie std::ceilund um die Verwendung immer abzurunden std::floor. Mein Beispiel von oben ist das Runden mitstd::round .

Erstellen Sie die Vorlagenfunktion "Aufrunden" oder besser bekannt als "Runde Decke" wie unten gezeigt:

template<typename T>
T roundCeilMultiple( T value, T multiple )
{
    if (multiple == 0) return value;
    return static_cast<T>(std::ceil(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
}

Erstellen Sie die Vorlagenfunktion "Abrunden" oder besser bekannt als "Abgerundeter Boden" wie unten gezeigt:

template<typename T>
T roundFloorMultiple( T value, T multiple )
{
    if (multiple == 0) return value;
    return static_cast<T>(std::floor(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
}
Flovdis
quelle
1
plus 1, obwohl einige Leute es vielleicht vernünftiger finden, 0 zurückzugeben, wenn mulitple == 0
stijn
3
Beachten Sie, dass die Konvertierung von int64_t in double verlustbehaftet sein kann und daher nicht ganz so generisch ist, wie es scheint.
Adrian McCarthy
@AdrianMcCarthy Ja, Sie müssen wie oben gezeigt die richtigen Vorlagenspezialisierungen erstellen. Wie Sie sehen, implementiere ich zwei zusätzliche Funktionen für long longund long double. Das gleiche muss natürlich für die beiden anderen Funktionen gemacht werden.
Flovdis
Ich denke, das ist bei weitem das langsamste von allen, aber es müsste nicht sein. Sie müssen lediglich std :: enable_if_t ausführen und zwei Zweige für Ganzzahlen und Gleitkommazahlen ausführen. Sie können auch die numerischen_Limits besser nutzen und prüfen, ob die Mantisse groß genug ist, um tatsächlich zum Wert zu passen. Das würde die Sicherheit erhöhen.
das Schwein
5

Zunächst einmal sollte Ihre Fehlerbedingung (multiple == 0) wahrscheinlich einen Rückgabewert haben. Was? Ich weiß es nicht. Vielleicht möchten Sie eine Ausnahme auslösen, das liegt bei Ihnen. Aber nichts zurückzugeben ist gefährlich.

Zweitens sollten Sie überprüfen, ob numToRound noch kein Vielfaches ist. Andernfalls , wenn Sie hinzufügen multiplezuroundDown , haben Sie die falsche Antwort bekommen.

Drittens sind Ihre Darsteller falsch. Du hast gegossennumToRound in eine Ganzzahl umgewandelt, aber es ist bereits eine Ganzzahl. Sie müssen vor der Division auf Double und nach der Multiplikation auf Int zurücksetzen.

Was möchten Sie zum Schluss für negative Zahlen? Aufrunden "aufrunden" kann auf Null runden (in die gleiche Richtung wie positive Zahlen runden) oder von Null weg (eine "größere" negative Zahl). Oder vielleicht ist es dir egal.

Hier ist eine Version mit den ersten drei Korrekturen, aber ich beschäftige mich nicht mit dem negativen Problem:

int roundUp(int numToRound, int multiple)
{
 if(multiple == 0)
 {
  return 0;
 }
 else if(numToRound % multiple == 0)
 {
  return numToRound
 }

 int roundDown = (int) (( (double) numToRound / multiple ) * multiple);
 int roundUp = roundDown + multiple; 
 int roundCalc = roundUp;
 return (roundCalc);
}
Mike Caron
quelle
@ Peter Ist es? Ich nahm an, dass int / intdies ein int zurückgeben würde, was wir nicht wollten.
Mike Caron
int / int gibt zwar ein int zurück, aber genau das möchten Sie. Zum Beispiel numToRound = 7, multiple = 3. 7/3 = 2.
Peter Ruderman
4

Runde zur Zweierpotenz:

Nur für den Fall, dass jemand eine Lösung für positive Zahlen benötigt, die auf das nächste Vielfache einer Zweierpotenz gerundet sind (denn so bin ich hier gelandet):

// number: the number to be rounded (ex: 5, 123, 98345, etc.)
// pow2:   the power to be rounded to (ex: to round to 16, use '4')
int roundPow2 (int number, int pow2) {
    pow2--;                     // because (2 exp x) == (1 << (x -1))
    pow2 = 0x01 << pow2;

    pow2--;                     // because for any
                                //
                                // (x = 2 exp x)
                                //
                                // subtracting one will
                                // yield a field of ones
                                // which we can use in a
                                // bitwise OR

    number--;                   // yield a similar field for
                                // bitwise OR
    number = number | pow2;
    number++;                   // restore value by adding one back

    return number;
}

Die eingegebene Nummer bleibt gleich, wenn es sich bereits um ein Vielfaches handelt.

Hier ist die x86_64-Ausgabe, die GCC mit -O2oder gibt -Os(9Sep2013 Build - godbolt GCC online):

roundPow2(int, int):
    lea ecx, [rsi-1]
    mov eax, 1
    sub edi, 1
    sal eax, cl
    sub eax, 1
    or  eax, edi
    add eax, 1
    ret

Jede C-Codezeile entspricht perfekt der Zeile in der Assembly: http://goo.gl/DZigfX

Jede dieser Anweisungen ist extrem schnell , daher ist die Funktion auch extrem schnell. Da der Code so klein und schnell ist, kann er für inlinedie Funktion bei der Verwendung hilfreich sein .


Anerkennung:

haneefmubarak
quelle
1
Genau das, wonach ich gesucht habe. Vielen Dank!
Kiyo
1
int roundUpPow2 (int num, int pow2) {return num + (pow2 - 1) & ~ (pow2 - 1); } ist ungefähr 30% schneller und einfacher zu verwenden (Sie übergeben 16 statt 4, um auf das nächste Vielfache von 16
aufzurunden.
3

Ich benutze:

template <class _Ty>
inline _Ty n_Align_Up(_Ty n_x, _Ty n_alignment)
{
    assert(n_alignment > 0);
    //n_x += (n_x >= 0)? n_alignment - 1 : 1 - n_alignment; // causes to round away from zero (greatest absolute value)
    n_x += (n_x >= 0)? n_alignment - 1 : -1; // causes to round up (towards positive infinity)
    //n_x += (_Ty(-(n_x >= 0)) & n_alignment) - 1; // the same as above, avoids branch and integer multiplication
    //n_x += n_alignment - 1; // only works for positive numbers (fastest)
    return n_x - n_x % n_alignment; // rounds negative towards zero
}

und für Zweierkräfte:

template <class _Ty>
bool b_Is_POT(_Ty n_x)
{
    return !(n_x & (n_x - 1));
}

template <class _Ty>
inline _Ty n_Align_Up_POT(_Ty n_x, _Ty n_pot_alignment)
{
    assert(n_pot_alignment > 0);
    assert(b_Is_POT(n_pot_alignment)); // alignment must be power of two
    -- n_pot_alignment;
    return (n_x + n_pot_alignment) & ~n_pot_alignment; // rounds towards positive infinity (i.e. negative towards zero)
}

Beachten Sie, dass beide negativen Werte gegen Null runden (dh für alle Werte auf unendlich runden). Keiner von beiden basiert auf einem vorzeichenbehafteten Überlauf (der in C / C ++ undefiniert ist).

Das gibt:

n_Align_Up(10, 100) = 100
n_Align_Up(110, 100) = 200
n_Align_Up(0, 100) = 0
n_Align_Up(-10, 100) = 0
n_Align_Up(-110, 100) = -100
n_Align_Up(-210, 100) = -200
n_Align_Up_POT(10, 128) = 128
n_Align_Up_POT(130, 128) = 256
n_Align_Up_POT(0, 128) = 0
n_Align_Up_POT(-10, 128) = 0
n_Align_Up_POT(-130, 128) = -128
n_Align_Up_POT(-260, 128) = -256
das Schwein
quelle
Ich benutze deine n_Align_Up_POT, seit ich sie in Delphis TList-Klasse gesehen habe. Es hat seine Einschränkungen, wie die Ausrichtung (mehrfach) eine Potenz von 2 ist, aber das ist selten ein Problem, da ich es meistens benutze, um die richtige Ausrichtung für SMID zu erhalten / zu überprüfen. Es ist großartig und es scheint, dass nicht viele Leute davon wissen.
user1593842
2

Wahrscheinlich sicherer in Floats umzuwandeln und Ceil () zu verwenden - es sei denn, Sie wissen, dass die Int-Division das richtige Ergebnis liefert.

Martin Beckett
quelle
1
Beachten Sie, dass double auf x86-basierten Computern nur 54 Bit Signifikand enthalten kann. Wenn Sie 64-Bit-Ints haben, schlägt dies letztendlich fehl.
das Schwein
IEEE754 Standard Double kann nicht, aber x64 CPU hat einen internen 80-Bit-Gleitkomma, so dass Operationen mit einer einzelnen Zahl zuverlässig sind
Martin Beckett
1
Während dies zutrifft, haben Sie nur sehr wenig Kontrolle über diese Rundung von C / C ++. Dies hängt von den Steuerworteinstellungen ab und kann tatsächlich auf weniger als 80 Bit gerundet werden. Sie haben auch SSE und andere SIMD-Befehlssätze, die keine solche erweiterte Zwischenstufe haben (der Vektorisierungs-Compiler könnte sie leicht verwenden).
das Schwein
2
int noOfMultiples = int((numToRound / multiple)+0.5);
return noOfMultiples*multiple

C ++ rundet jede Zahl ab. Wenn Sie also 0,5 hinzufügen (wenn es 1,5 ist, ist es 2), aber 1,49 ist 1,99, also 1.

BEARBEITEN - Entschuldigung, Sie haben nicht gesehen, dass Sie aufrunden möchten. Ich würde vorschlagen, anstelle von +0.5 eine Ceil () -Methode zu verwenden

Michal Ciechan
quelle
2

Zum einen, da ich nicht wirklich verstehe, was Sie tun wollen, die Zeilen

int roundUp = roundDown + multiple;
int roundCalc = roundUp;
return (roundCalc); 

könnte definitiv verkürzt werden

int roundUp = roundDown + multiple;
return roundUp;
Jesse Naugher
quelle
2

Vielleicht kann dies helfen:

int RoundUpToNearestMultOfNumber(int val, int num)
{
  assert(0 != num);
  return (floor((val + num) / num) * num);
}
Arsen
quelle
Warum Floor und Integer Division verwenden? Es gibt nichts zu Boden. Wenn es doppelt wäre, könnten Sie zumindest die Behandlung negativer Werte erben.
das Schwein
2

Immer abrunden

int alwaysRoundUp(int n, int multiple)
{
    if (n % multiple != 0) {
        n = ((n + multiple) / multiple) * multiple;

        // Another way
        //n = n - n % multiple + multiple;
    }

    return n;
}

alwaysRoundUp (1, 10) -> 10

alwaysRoundUp (5, 10) -> 10

alwaysRoundUp (10, 10) -> 10


Immer abrunden

int alwaysRoundDown(int n, int multiple)
{
    n = (n / multiple) * multiple;

    return n;
}

alwaysRoundDown (1, 10) -> 0

alwaysRoundDown (5, 10) -> 0

alwaysRoundDown (10, 10) -> 10


Den normalen Weg abrunden

int normalRound(int n, int multiple)
{
    n = ((n + multiple/2)/multiple) * multiple;

    return n;
}

normalRound (1, 10) -> 0

normalRound (5, 10) -> 10

normalRound (10, 10) -> 10

onmyway133
quelle
2

Auf das nächste Vielfache runden, das zufällig eine Potenz von 2 ist

unsigned int round(unsigned int value, unsigned int multiple){
    return ((value-1u) & ~(multiple-1u)) + multiple;
}

Dies kann nützlich sein, wenn Sie entlang von Junggesellen zuordnen, bei denen das gewünschte Rundungsinkrement eine Zweierpotenz ist, der resultierende Wert jedoch nur ein Vielfaches davon sein muss. Auf gccdem Körper dieser Funktion werden 8 Montageanweisungen ohne Unterteilung oder Verzweigungen generiert.

round(  0,  16) ->   0
round(  1,  16) ->  16
round( 16,  16) ->  16
round(257, 128) -> 384 (128 * 3)
round(333,   2) -> 334
Anne Quinn
quelle
1

Ich habe einen Algorithmus gefunden, der dem oben beschriebenen etwas ähnlich ist:

int [(| x | + n-1) / n] * [(nx) / | x |], wobei x ein Benutzereingabewert und n das verwendete Vielfache ist.

Es funktioniert für alle Werte x, wobei x eine ganze Zahl ist (positiv oder negativ, einschließlich Null). Ich habe es speziell für ein C ++ - Programm geschrieben, aber dies kann grundsätzlich in jeder Sprache implementiert werden.

Joshua Wade
quelle
1

Für negative numToRound:

Es sollte wirklich einfach sein, dies zu tun, aber der Standard-Modulo% -Operator verarbeitet negative Zahlen nicht wie erwartet. Zum Beispiel -14% 12 = -2 und nicht 10. Als erstes müssen Sie einen Modulo-Operator erhalten, der niemals negative Zahlen zurückgibt. Dann ist roundUp wirklich einfach.

public static int mod(int x, int n) 
{
    return ((x % n) + n) % n;
}

public static int roundUp(int numToRound, int multiple) 
{
    return numRound + mod(-numToRound, multiple);
}
user990343
quelle
1

Das würde ich tun:

#include <cmath>

int roundUp(int numToRound, int multiple)
{
    // if our number is zero, return immediately
   if (numToRound == 0)
        return multiple;

    // if multiplier is zero, return immediately
    if (multiple == 0)
        return numToRound;

    // how many times are number greater than multiple
    float rounds = static_cast<float>(numToRound) / static_cast<float>(multiple);

    // determine, whether if number is multiplier of multiple
    int floorRounds = static_cast<int>(floor(rounds));

    if (rounds - floorRounds > 0)
        // multiple is not multiplier of number -> advance to the next multiplier
        return (floorRounds+1) * multiple;
    else
        // multiple is multiplier of number -> return actual multiplier
        return (floorRounds) * multiple;
}

Der Code ist möglicherweise nicht optimal, aber ich bevorzuge sauberen Code gegenüber trockener Leistung.

Erwischt
quelle
Das Casting des intto floatverliert leicht an Präzision und führt zu falschen Antworten.
chux
1
int roundUp (int numToRound, int multiple)
{
  return multiple * ((numToRound + multiple - 1) / multiple);
}

obwohl:

  • funktioniert nicht für negative Zahlen
  • funktioniert nicht, wenn numRound + mehrere Überläufe

würde vorschlagen, stattdessen vorzeichenlose Ganzzahlen zu verwenden, die das Überlaufverhalten definiert haben.

Sie erhalten eine Ausnahme von multiplen == 0, aber in diesem Fall ist dies ohnehin kein genau definiertes Problem.

user3392484
quelle
1

c:

int roundUp(int numToRound, int multiple)
{
  return (multiple ? (((numToRound+multiple-1) / multiple) * multiple) : numToRound);
}

und für dein ~ / .bashrc:

roundup()
{
  echo $(( ${2} ? ((${1}+${2}-1)/${2})*${2} : ${1} ))
}
nhed
quelle
1

Ich verwende eine Kombination von Modulen, um die Addition des Restes aufzuheben, wenn xes sich bereits um ein Vielfaches handelt:

int round_up(int x, int div)
{
    return x + (div - x % div) % div;
}

Wir finden die Umkehrung des Rest-dann-Moduls, die mit dem Divisor erneut aufgehoben wird, wenn es sich um den Divisor selbst handelt, und fügen dann hinzu x.

round_up(19, 3) = 21
Nick Bedford
quelle
1

Hier ist meine Lösung basierend auf dem Vorschlag des OP und den Beispielen aller anderen. Da fast jeder danach suchte, negative Zahlen zu verarbeiten, macht diese Lösung genau das, ohne die Verwendung spezieller Funktionen, dh abs und dergleichen.

Wenn Sie den Modul vermeiden und stattdessen die Division verwenden, ist die negative Zahl ein natürliches Ergebnis, obwohl sie abgerundet ist. Nachdem die abgerundete Version berechnet wurde, führt sie die erforderliche Berechnung durch, um entweder in negativer oder positiver Richtung aufzurunden.

Beachten Sie auch, dass keine speziellen Funktionen zum Berechnen verwendet werden, sodass dort ein kleiner Geschwindigkeitsschub erfolgt.

int RoundUp(int n, int multiple)
{
    // prevent divide by 0 by returning n
    if (multiple == 0) return n;

    // calculate the rounded down version
    int roundedDown = n / multiple * multiple;

    // if the rounded version and original are the same, then return the original
    if (roundedDown == n) return n;

    // handle negative number and round up according to the sign
    // NOTE: if n is < 0 then subtract the multiple, otherwise add it
    return (n < 0) ? roundedDown - multiple : roundedDown + multiple;
}
weatx
quelle
Schlägt mit RoundUp(INT_MIN, -1)als n / multipleist intÜberlauf.
chux
1

Ich denke, das sollte dir helfen. Ich habe das folgende Programm in C geschrieben.

# include <stdio.h>
int main()
{
  int i, j;
  printf("\nEnter Two Integers i and j...");
  scanf("%d %d", &i, &j);
  int Round_Off=i+j-i%j;
  printf("The Rounded Off Integer Is...%d\n", Round_Off);
  return 0;
}
Neel
quelle
0
/// Rounding up 'n' to the nearest multiple of number 'b'.
/// - Not tested for negative numbers.
/// \see http://stackoverflow.com/questions/3407012/
#define roundUp(n,b) ( (b)==0 ? (n) : ( ((n)+(b)-1) - (((n)-1)%(b)) ) )

/// \c test->roundUp().
void test_roundUp() {   
    // yes_roundUp(n,b) ( (b)==0 ? (n) : ( (n)%(b)==0 ? n : (n)+(b)-(n)%(b) ) )
    // yes_roundUp(n,b) ( (b)==0 ? (n) : ( ((n + b - 1) / b) * b ) )

    // no_roundUp(n,b) ( (n)%(b)==0 ? n : (b)*( (n)/(b) )+(b) )
    // no_roundUp(n,b) ( (n)+(b) - (n)%(b) )

if (true) // couldn't make it work without (?:)
{{  // test::roundUp()
    unsigned m;
                { m = roundUp(17,8); } ++m;
    assertTrue( 24 == roundUp(17,8) );
                { m = roundUp(24,8); }
    assertTrue( 24 == roundUp(24,8) );

    assertTrue( 24 == roundUp(24,4) );
    assertTrue( 24 == roundUp(23,4) );
                { m = roundUp(23,4); }
    assertTrue( 24 == roundUp(21,4) );

    assertTrue( 20 == roundUp(20,4) );
    assertTrue( 20 == roundUp(19,4) );
    assertTrue( 20 == roundUp(18,4) );
    assertTrue( 20 == roundUp(17,4) );

    assertTrue( 17 == roundUp(17,0) );
    assertTrue( 20 == roundUp(20,0) );
}}
}
Adolfo
quelle
0

Dadurch erhalten Sie die gewünschten Ergebnisse für positive Ganzzahlen:

#include <iostream>
using namespace std;

int roundUp(int numToRound, int multiple);

int main() {
    cout << "answer is: " << roundUp(7, 100) << endl;
    cout << "answer is: " << roundUp(117, 100) << endl;
    cout << "answer is: " << roundUp(477, 100) << endl;
    cout << "answer is: " << roundUp(1077, 100) << endl;
    cout << "answer is: " << roundUp(52,20) << endl;
    cout << "answer is: " << roundUp(74,30) << endl;
    return 0;
}

int roundUp(int numToRound, int multiple) {
    if (multiple == 0) {
        return 0;
    }
    int result = (int) (numToRound / multiple) * multiple;
    if (numToRound % multiple) {
        result += multiple;
    } 
    return result;
}

Und hier sind die Ausgaben:

answer is: 100
answer is: 200
answer is: 500
answer is: 1100
answer is: 60
answer is: 90
Dave
quelle
-1

Dies funktioniert bei mir, hat aber nicht versucht, mit Negativen umzugehen

public static int roundUp(int numToRound, int multiple) {
    if (multiple == 0) {
        return 0;
    } else if (numToRound % multiple == 0) {
    return numToRound;
    }

    int mod = numToRound % multiple;
    int diff = multiple - mod;
    return numToRound + diff;
}
user1100538
quelle
-2

Hier ist eine super einfache Lösung, um das Konzept der Eleganz zu zeigen. Es ist im Grunde für Grid-Snaps.

(Pseudocode)

nearestPos = Math.Ceil( numberToRound / multiple ) * multiple;
Indivelope
quelle
Haben Sie Ihre Idee vor dem Einreichen überprüft? es kann nicht die richtige Antwort geben
yaodav