C ++: arrotondamento al multiplo più vicino di un numero

OK – Sono quasi in imbarazzo a postare questo qui (e cancellerò se qualcuno voterà per chiudere) come sembra una domanda di base.

È questo il modo corretto per arrotondare a un multiplo di un numero in C ++?

So che ci sono altre domande relative a questo, ma sono curiosamente interessato a sapere qual è il modo migliore per farlo in C ++:

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

Aggiornamento: Scusa probabilmente non ho chiarito l’intenzione. Ecco alcuni esempi:

 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: Grazie per tutte le risposte. Ecco cosa sono andato a:

 int roundUp(int numToRound, int multiple) { if(multiple == 0) { return numToRound; } int remainder = numToRound % multiple; if (remainder == 0) { return numToRound; } return numToRound + multiple - remainder; } 

Funziona per numeri positivi, non sono sicuro del negativo. Utilizza solo la matematica intera.

 int roundUp(int numToRound, int multiple) { if (multiple == 0) return numToRound; int remainder = numToRound % multiple; if (remainder == 0) return numToRound; return numToRound + multiple - remainder; } 

Modifica: Ecco una versione che funziona con numeri negativi, se con “su” intendi un risultato che è sempre> = l’input.

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

Senza condizioni:

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

Funziona come l’ arrotondamento da zero per i numeri negativi

EDIT: versione che funziona anche per i numeri negativi

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

test


Se multiple è una potenza di 2

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

test

Funziona quando il fattore sarà sempre positivo:

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

Questa è una generalizzazione del problema di “come faccio a scoprire quanti byte n bit prenderanno?” (A: (n bit + 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 } 
 int roundUp(int numToRound, int multiple) { if(multiple == 0) { return 0; } return ((numToRound - 1) / multiple + 1) * multiple; } 

E non c’è bisogno di scherzare con le condizioni

Per chiunque cerchi una risposta breve e dolce. Questo è quello che ho usato. Non contabilizza i negativi.

 n - (n % r) 

Ciò restituirà il fattore precedente.

 (n + r) - (n % r) 

Restituirà il prossimo. Spero che questo aiuti qualcuno. 🙂

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

Funziona con qualsiasi numero o base mobile (ad es. Puoi arrotondare -4 al più vicino 6,75). In sostanza si sta convertendo in punto fisso, arrotondando lì, quindi convertendo indietro. Gestisce i negativi arrotondando AWAY da 0. Gestisce anche un round negativo al valore essenzialmente trasformando la funzione in roundDown.

Una versione specifica int si presenta come:

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

Quale è più o meno la risposta del plinth, con il supporto di input negativo aggiunto.

Questo è il moderno approccio c ++ che utilizza una funzione template che funziona per float, double, long, int e short (ma non per long long e long double a causa dei doppi valori usati).

 #include  #include  template T roundMultiple( T value, T multiple ) { if (multiple == 0) return value; return static_cast(std::round(static_cast(value)/static_cast(multiple))*static_cast(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; } 

Ma puoi facilmente aggiungere il supporto per il long long e long double con la specializzazione del modello come mostrato di seguito:

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

Per creare funzioni da arrotondare, usa std::ceil e per arrotondare sempre verso il basso usa std::floor . Il mio esempio di cui sopra è l'arrotondamento usando std::round .

Crea la funzione "round up" o meglio conosciuta come "round ceiling" come mostrato di seguito:

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

Crea la funzione "round down" o meglio conosciuta come "round floor" come mostrato di seguito:

 template T roundFloorMultiple( T value, T multiple ) { if (multiple == 0) return value; return static_cast(std::floor(static_cast(value)/static_cast(multiple))*static_cast(multiple)); } 

Prima di tutto, la tua condizione di errore (più == 0) dovrebbe probabilmente avere un valore di ritorno. Che cosa? Non lo so. Forse vuoi fare un’eccezione, dipende da te. Ma restituire nulla è pericoloso.

Secondo, dovresti controllare che numToRound non sia già un multiplo. Altrimenti, quando aggiungi multiple a roundDown , otterrai la risposta sbagliata.

Terzo, i tuoi cast sono sbagliati. numToRound su un numero intero, ma è già un numero intero. Devi castare per raddoppiare prima della divisione e tornare a int dopo la moltiplicazione.

Infine, cosa vuoi per i numeri negativi? Arrotondare “su” può significare arrotondare a zero (arrotondando nella stessa direzione dei numeri positivi) o lontano da zero (un numero negativo “più grande”). O forse non ti interessa.

Ecco una versione con le prime tre correzioni, ma non mi occupo del problema negativo:

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

Intorno al potere di due:

Nel caso qualcuno abbia bisogno di una soluzione per numeri positivi arrotondati al multiplo più vicino di una potenza di due (perché è così che sono finito qui):

 // 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; } 

Il numero di input rimarrà lo stesso se è già un multiplo.

Ecco l'output x86_64 -Os GCC con -O2 o -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 

Ogni riga di codice C corrisponde perfettamente alla sua linea nell'assembly: http://goo.gl/DZigfX

Ciascuna di queste istruzioni è estremamente veloce , quindi anche la funzione è estremamente veloce. Poiché il codice è così piccolo e veloce, potrebbe essere utile inline la funzione quando lo si utilizza.


Credito:

  • Algoritmo: Hagen von Eitzen @ Math.SE
  • Compilatore interattivo Godbolt : @ mattgodbolt / gcc-explorer su GitHub

Sto usando:

 template  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 } 

e per le potenze di due:

 template  bool b_Is_POT(_Ty n_x) { return !(n_x & (n_x - 1)); } template  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 (ie negative towards zero) } 

Notare che entrambi i valori negativi tondi verso zero (che significa arrotondamento all’infinito positivo per tutti i valori), nessuno dei due si basa sull’overflow con segno (che non è definito in C / C ++).

Questo da:

 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 

Probabilmente è più sicuro lanciare su float e usare ceil () – a meno che tu non sappia che la divisione int produrrà il risultato corretto.

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

C ++ arrotonda ogni numero in basso, quindi se aggiungi 0.5 (se è 1.5 sarà 2) ma 1.49 sarà 1.99 quindi 1.

EDIT – Scusa non ho visto che volevi completare, ti suggerirei di usare un metodo ceil () invece del +0.5

beh per una cosa, dato che non capisco davvero cosa vuoi fare, le linee

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

potrebbe essere abbreviato in

 int roundUp = roundDown + multiple; return roundUp; 

potrebbe essere questo può aiutare:

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

Per arrotondare sempre

 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


Per arrotondare sempre

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

alwaysRoundDown (1, 10) -> 0

alwaysRoundDown (5, 10) -> 0

alwaysRoundDown (10, 10) -> 10


Per arrotondare nel modo normale

 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

Ho trovato un algoritmo che è in qualche modo simile a quello pubblicato sopra:

int [(| x | + n-1) / n] * [(nx) / | x |], dove x è un valore di input dell’utente e n è il multiplo utilizzato.

Funziona con tutti i valori x, dove x è un numero intero (positivo o negativo, compreso lo zero). L’ho scritto specificamente per un programma C ++, ma questo può essere implementato praticamente in qualsiasi lingua.

Per numToRound negativo:

Dovrebbe essere davvero facile farlo, ma l’operatore modulo% standard non gestisce numeri negativi come ci si potrebbe aspettare. Ad esempio -14% 12 = -2 e non 10. La prima cosa da fare è ottenere l’operatore modulo che non restituisce mai numeri negativi. Quindi roundUp è davvero semplice.

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

Questo è quello che farei:

 #include  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(numToRound) / static_cast(multiple); // determine, whether if number is multiplier of multiple int floorRounds = static_cast(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; } 

Il codice potrebbe non essere ottimale, ma preferisco il codice pulito rispetto alle prestazioni a secco.

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

sebbene:

  • non funzionerà con numeri negativi
  • non funzionerà se numRound + overflow multipli

suggerirebbe invece di utilizzare interi senza segno, che ha definito il comportamento di overflow.

Otterrai un’eccezione di più == 0, ma in questo caso non si tratta comunque di un problema ben definito.

c:

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

e per il tuo ~ / .bashrc:

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

Io uso una combinazione di moduli per annullare l’aggiunta del resto se x è già un multiplo:

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

Troviamo l’inverso del resto, quindi moduliamo quello con il divisore di nuovo per annullarlo se è il divisore stesso, quindi aggiungiamo x .

 round_up(19, 3) = 21 

Ecco la mia soluzione basata sul suggerimento dell’OP e sugli esempi forniti da tutti gli altri. Dal momento che molti lo cercavano per gestire numeri negativi, questa soluzione fa proprio questo, senza l’uso di alcuna funzione speciale, cioè abs e simili.

Evitando il modulo e usando la divisione, invece, il numero negativo è un risultato naturale, sebbene sia arrotondato per difetto. Dopo aver calcolato la versione arrotondata, esegue la matematica necessaria per arrotondare, in direzione negativa o positiva.

Si noti inoltre che non vengono utilizzate funzioni speciali per calcolare nulla, quindi c’è un piccolo aumento di velocità lì.

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

Questo sta ottenendo i risultati che stai cercando per numeri interi positivi:

 #include  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; } 

E qui ci sono le uscite:

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

Questo funziona per me ma non ho provato a gestire i negativi

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