it-swarm.com.de

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

OK - es ist fast peinlich, das hier zu posten (und ich werde es streichen, wenn sich jemand dafür entscheidet, zu schließen), da es wie eine grundlegende Frage erscheint.

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

Ich weiß, dass es andere Fragen gibt, die damit in Zusammenhang stehen, aber ich bin speziell 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

EDIT: Danke für alle Antworten. Ich habe folgendes getan:

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

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

 return numToRound + multiple - remainder; 
}  
142

Dies funktioniert bei positiven Zahlen, bei negativen Zahlen nicht. 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;
}

Edit: Hier ist eine Version, die mit negativen Zahlen arbeitet, wenn mit "up" ein Ergebnis gemeint ist, 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;
}
140
Mark Ransom

Ohne Bedingungen:

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

Dies funktioniert wie Abrundung 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 multiple eine Potenz von 2 ist

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

Tests

92
KindDragon

Dies funktioniert, wenn der Faktor immer positiv ist:

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

Edit: Dies gibt round_up(0,100)=100 zurück. In dem Kommentar von Paulus finden Sie eine Lösung, die round_up(0,100)=0 zurückgibt.

33
xlq

Dies ist eine Verallgemeinerung des Problems "Wie erfahre ich, wie viele Bytes n Bits benötigen (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
}
22
plinth
int roundUp(int numToRound, int multiple)
{
 if(multiple == 0)
 {
  return 0;
 }
 return ((numToRound - 1) / multiple + 1) * multiple;  
}

Und keine Notwendigkeit, sich mit den Bedingungen herumzumachen

14
doron
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 die nächsten 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 zum Wert, indem die Funktion 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 des Sockels mit der zusätzlichen negativen Eingabeunterstützung.

9
Dolphin

Für alle, die eine kurze und süße Antwort suchen. Das habe ich benutzt. Keine Abrechnung für Negative.

n - (n % r)

Das wird den vorherigen Faktor zurückgeben.

(n + r) - (n % r)

Werde den nächsten wiederkommen. Hoffe das hilft jemandem. :)

8
aaron-bond

Dies ist der moderne C++ - Ansatz, der eine Template-Funktion verwendet, die für Float, Double, Long, Int und Short (jedoch nicht für Long Long und Long Double aufgrund der verwendeten Double-Werte) funktioniert.

#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 long und long double mit Schablonenspezialisierung 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));
}

Zum Erstellen von Funktionen zum Aufrunden verwenden Sie std::ceil und zum Abrunden immer std::floor. Mein Beispiel von oben ist das Runden mit std::round.

Erstellen Sie die Vorlagenfunktion "Aufrunden" oder besser als "runde Decke" (siehe unten):

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 "Rundboden" (siehe unten):

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));
}
7
Flovdis

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

Zweitens sollten Sie überprüfen, dass numToRound nicht bereits ein Vielfaches ist. Andernfalls erhalten Sie die falsche Antwort, wenn Sie multiple zu roundDown hinzufügen.

Drittens sind deine Abgüsse falsch. Sie wandeln numToRound in eine Ganzzahl um, aber es ist bereits eine Ganzzahl. Sie müssen vor der Division eine Verdoppelung durchführen und nach der Multiplikation wieder auf int.

Was wollen Sie schließlich für negative Zahlen? Das Aufrunden von "Aufrunden" kann bedeuten, dass auf Null gerundet wird (in dieselbe Richtung wie positive Zahlen gerundet) oder von Null weg (eine "größere" negative Zahl). Oder vielleicht interessiert es dich nicht.

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);
}
5
Mike Caron

Runden Sie auf Zwei!

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 werden (weil ich hier so gelandet bin):

// 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 bereits ein Vielfaches ist.

Hier ist die x86_64-Ausgabe, die GCC mit -O2 oder -Os (9Sep2013 Build - godbolt GCC online) ausgibt:

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 stimmt perfekt mit der Zeile in der Assembly überein: 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 es nützlich sein, die Funktion inline zu verwenden, wenn er verwendet wird.


Kredit:

4
haneefmubarak

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 zwei Personen:

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 dieser negativen Werte gegen Null runden (dh für alle Werte auf eine positive Unendlichkeit gerundet werden). Keiner von ihnen beruht 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
3
the swine

Immer aufrunden

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

normalRunde (5, 10) -> 10

normalRunde (10, 10) -> 10

2
onmyway133

kann dies helfen:

int RoundUpToNearestMultOfNumber(int val, int num)
{
  assert(0 != num);
  return (floor((val + num) / num) * num);
}
2
Arsen

Wahrscheinlich sicherer, um Floats auszuführen und Ceil () zu verwenden - es sei denn, Sie wissen, dass die int-Division das korrekte Ergebnis liefert.

2
Martin Beckett
int noOfMultiples = int((numToRound / multiple)+0.5);
return noOfMultiples*multiple

C++ rundet jede Zahl ab. Wenn Sie also 0,5 addieren (bei 1,5 sind es 2), werden bei 1,49 1,99 und somit 1 angegeben.

BEARBEITEN - Entschuldigung, dass Sie nicht abschließen wollten, würde ich empfehlen, eine ceil () -Methode anstelle der +0.5 zu verwenden

2
Michal Ciechan

gut für eine Sache, da ich nicht wirklich verstehe, was Sie wollen, die Zeilen 

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

könnte auf jeden Fall verkürzt werden 

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

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 sauberer Code als trockene Leistung.

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

obwohl:

  • funktioniert nicht für negative Zahlen
  • funktioniert nicht, wenn numRound + mehrfach überläuft

würde anstelle von vorzeichenlosen Ganzzahlen vorschlagen, die das Überlaufverhalten definiert haben.

Eine Ausnahme ist multiple == 0, aber in diesem Fall ist es sowieso kein genau definiertes Problem.

1
user3392484

Ich benutze eine Kombination von Modul, um den Zusatz des Restes zu annullieren, wenn x bereits ein Vielfaches ist:

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

Wir finden die Umkehrung des Restmoduls dann mit dem Divisor erneut, um ihn zu annullieren, wenn es der Divisor selbst ist, und dann x hinzufügen.

round_up(19, 3) = 21
1
Nick Bedford

Ich habe einen Algorithmus gefunden, der dem oben genannten ähnelt:

int [(| x | + n-1)/n] * [(nx)/| x |], wobei x ein vom Benutzer eingegebener Wert ist und n das verwendete Vielfach 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, dies kann jedoch grundsätzlich in jeder Sprache implementiert werden.

1
Joshua Wade

Hier ist meine Lösung basierend auf dem Vorschlag des OP und den Beispielen, die von allen anderen gegeben wurden. Da fast jeder es suchte, um mit negativen Zahlen umzugehen, macht diese Lösung genau das, ohne spezielle Funktionen, d. H. Bauchmuskeln und dergleichen, zu verwenden.

Durch die Vermeidung des Moduls und die Verwendung einer Division wird die negative Zahl natürlich, obwohl sie abgerundet ist. Nachdem die abgerundete Version berechnet wurde, führt sie die erforderlichen Berechnungen aus, entweder in negativer oder in positiver Richtung.

Beachten Sie auch, dass keine speziellen Funktionen verwendet werden, um etwas zu berechnen, daher gibt es dort eine kleine Geschwindigkeitssteigerung.

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;
}
1
weatx

c:

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

und für Ihre ~/.bashrc:

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

Für negative numToRound:

Es sollte sehr einfach sein, dies zu tun, aber der Standard-Modulo-Operator% verarbeitet keine negativen Zahlen, wie man erwarten könnte. 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 sehr 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);
}
1
user990343

Zum nächsten Vielfachen rollen, 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 Cachelines zuweisen, wobei das Rundungsinkrement eine Zweierpotenz ist, der resultierende Wert jedoch nur ein Vielfaches davon sein muss. Bei gcc generiert der Körper dieser Funktion 8 Montageanweisungen ohne Abteilungen oder Verzweigungen.

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

Hier erhalten Sie die Ergebnisse, die Sie für positive ganze Zahlen suchen:

#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 Ausgänge:

answer is: 100
answer is: 200
answer is: 500
answer is: 1100
answer is: 60
answer is: 90
0
Dave

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;
}
0
Neel
/// 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) );
}}
}
0
Adolfo