OK – Je suis presque gêné de poster ceci ici (et je supprimerai si quelqu’un vote pour fermer) car cela semble être une question de base.
Est-ce la bonne façon de arrondir à un multiple d’un nombre en C ++?
Je sais qu’il y a d’autres questions liées à cela mais je suis particulièrement intéressé de savoir quelle est la meilleure façon de le faire en 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); }
Mise à jour: Désolé, je n’ai sans doute pas clarifié mon intention. Voici quelques exemples:
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: Merci pour toutes les réponses. Voici ce que je suis allé pour:
int roundUp(int numToRound, int multiple) { if(multiple == 0) { return numToRound; } int remainder = numToRound % multiple; if (remainder == 0) { return numToRound; } return numToRound + multiple - remainder; }
Cela fonctionne pour des nombres positifs, pas sûr de négatif. Il n’utilise que des mathématiques entières.
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: Voici une version qui fonctionne avec des nombres négatifs, si par “up” vous voulez dire un résultat qui est toujours> = l’entrée.
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; }
Sans conditions:
int roundUp(int numToRound, int multiple) { assert(multiple); return ((numToRound + multiple - 1) / multiple) * multiple; }
Cela fonctionne comme arrondir à zéro pour les nombres négatifs
EDIT: Version qui fonctionne aussi pour les nombres négatifs
int roundUp(int numToRound, int multiple) { assert(multiple); int isPositive = (int)(numToRound >= 0); return ((numToRound + isPositive * (multiple - 1)) / multiple) * multiple; }
Des tests
Si multiple
est une puissance de 2
int roundUp(int numToRound, int multiple) { assert(multiple && ((multiple & (multiple - 1)) == 0)); return (numToRound + multiple - 1) & -multiple; }
Des tests
Cela fonctionne quand le facteur sera toujours positif:
int round_up(int num, int factor) { return num + factor - 1 - (num - 1) % factor; }
Ceci est une généralisation du problème de “comment puis-je savoir combien d’octets n bits vont prendre? (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 }
int roundUp(int numToRound, int multiple) { if(multiple == 0) { return 0; } return ((numToRound - 1) / multiple + 1) * multiple; }
Et pas besoin de jouer avec les conditions
Pour ceux qui recherchent une réponse courte et douce. C’est ce que j’ai utilisé. Pas de comptabilité pour les négatifs.
n - (n % r)
Cela retournera le facteur précédent.
(n + r) - (n % r)
Reviendra le prochain. J’espère que cela aide quelqu’un. 🙂
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; }
Cela fonctionne pour n’importe quel nombre ou base de float (par exemple, vous pouvez arrondir -4 à 6.75 près). Essentiellement, il convertit en point fixe, arrondit là, puis se reconvertit. Il gère les négatifs en arrondissant AWAY à partir de 0. Il gère également un tour négatif à la valeur en transformant essentiellement la fonction en roundDown.
Une version int spécifique ressemble à:
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; }
Ce qui est plus ou moins la réponse du socle, avec le support des entrées négatives supplémentaires.
C’est l’approche c ++ moderne qui utilise une fonction template qui fonctionne pour float, double, long, int et short (mais pas pour longtemps, et long double à cause des valeurs doubles utilisées).
#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; }
Mais vous pouvez facilement append un support long long
et long double
spécialisation, comme indiqué ci-dessous:
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)); }
Pour créer des fonctions à arrondir, utilisez std::ceil
et arrondissez toujours l'utilisation std::floor
. Mon exemple ci-dessus est arrondi en utilisant std::round
.
Créez la fonction de modèle "Round Up" ou mieux connue sous le nom de "Round Round", comme indiqué ci-dessous:
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)); }
Créez le gabarit "rond" ou mieux connu sous le nom de "round floor" comme indiqué ci-dessous:
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)); }
Tout d’abord, votre condition d’erreur (multiple == 0) devrait probablement avoir une valeur de retour. Quelle? Je ne sais pas. Peut-être que vous voulez lancer une exception, c’est à vous de décider. Mais retourner rien n’est dangereux.
Deuxièmement, vous devriez vérifier que numToRound n’est pas déjà un multiple. Sinon, lorsque vous en ajoutez multiple
à roundDown
, vous obtiendrez une mauvaise réponse.
Troisièmement, vos lancers sont faux. Vous numToRound
en un entier, mais c’est déjà un entier. Vous devez lancer pour doubler avant la division et revenir à l’int après la multiplication.
Enfin, que voulez-vous pour les nombres négatifs? Arrondir “vers le haut” peut signifier un arrondi à zéro (arrondi dans la même direction que les nombres positifs), ou éloigné de zéro (un nombre négatif “plus grand”). Ou peut-être que vous vous en fichez.
Voici une version avec les trois premiers correctifs, mais je ne traite pas du problème négatif:
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); }
Juste au cas où quelqu’un aurait besoin d’une solution pour des nombres positifs arrondis au multiple le plus proche d’une puissance de deux (parce que c’est comme ça que je me suis retrouvé ici):
// 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; }
Le numéro d'entrée restra le même s'il s'agit déjà d'un multiple.
Voici la sortie x86_64 que GCC fournit avec -O2
ou -Os
(9Sep2013 Build - godbolt GCC en ligne):
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
Chaque ligne de code C correspond parfaitement à sa ligne dans l'assemblage: http://goo.gl/DZigfX
Chacune de ces instructions est extrêmement rapide , de sorte que la fonction est extrêmement rapide aussi. Étant donné que le code est si petit et rapide, il peut être utile d’inclure la fonction lorsque vous l’utilisez.
Crédit:
J’utilise:
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 }
et pour les pouvoirs de deux:
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) }
Notez que ces deux valeurs négatives arrondies vers zéro (ce qui signifie arrondi à l’infini positif pour toutes les valeurs), aucune ne repose sur un dépassement signé (qui n’est pas défini en C / C ++).
Cela donne:
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
Probablement plus sûr de lancer des flottants et d’utiliser ceil () – à moins que vous sachiez que la division int va produire le résultat correct.
int noOfMultiples = int((numToRound / multiple)+0.5); return noOfMultiples*multiple
C ++ arrondit chaque nombre vers le bas, donc si vous ajoutez 0,5 (si sa valeur de 1,5 correspond à 2), 1,49 sera donc 1,99 donc 1.
EDIT – Désolé je ne vous ai pas vu vouloir arrondir, je suggère d’utiliser une méthode ceil () au lieu de +0.5
bien pour une chose, puisque je ne comprends pas vraiment ce que vous voulez faire, les lignes
int roundUp = roundDown + multiple; int roundCalc = roundUp; return (roundCalc);
pourrait certainement être raccourci à
int roundUp = roundDown + multiple; return roundUp;
peut être que cela peut aider:
int RoundUpToNearestMultOfNumber(int val, int num) { assert(0 != num); return (floor((val + num) / num) * num); }
Arranger toujours
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
Arrondir toujours
int alwaysRoundDown(int n, int multiple) { n = (n / multiple) * multiple; return n; }
alwaysRoundDown (1, 10) -> 0
alwaysRoundDown (5, 10) -> 0
alwaysRoundDown (10, 10) -> 10
Pour contourner la voie 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
J’ai trouvé un algorithme quelque peu similaire à celui affiché ci-dessus:
int [(| x | + n-1) / n] * [(nx) / | x |], où x est une valeur saisie par l’utilisateur et n est le multiple utilisé.
Il fonctionne pour toutes les valeurs x, où x est un entier (positif ou négatif, y compris zéro). Je l’ai écrit spécifiquement pour un programme C ++, mais cela peut être implémenté dans n’importe quel langage.
Pour numToRound négatif:
Cela devrait être très simple, mais l’opérateur standard modulo% ne gère pas les nombres négatifs comme on pourrait s’y attendre. Par exemple -14% 12 = -2 et non 10. La première chose à faire est d’obtenir un opérateur modulo qui ne retourne jamais de nombres négatifs. Alors RoundUp est vraiment simple.
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); }
C’est ce que je ferais:
#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; }
Le code pourrait ne pas être optimal, mais je préfère le code propre que la performance sèche.
int roundUp (int numToRound, int multiple) { return multiple * ((numToRound + multiple - 1) / multiple); }
bien que:
suggérerait d’utiliser plutôt des entiers non signés, qui ont un comportement de débordement défini.
Vous obtiendrez une exception multiple == 0, mais ce n’est pas un problème bien défini dans ce cas de toute façon.
c:
int roundUp(int numToRound, int multiple) { return (multiple ? (((numToRound+multiple-1) / multiple) * multiple) : numToRound); }
et pour votre ~ / .bashrc:
roundup() { echo $(( ${2} ? ((${1}+${2}-1)/${2})*${2} : ${1} )) }
J’utilise une combinaison de module pour annuler l’addition du rest si x
est déjà un multiple:
int round_up(int x, int div) { return x + (div - x % div) % div; }
Nous trouvons l’inverse du rest puis nous modulons celui avec le diviseur pour l’annuler si c’est le diviseur lui-même alors ajoutez x
.
round_up(19, 3) = 21
Voici ma solution basée sur la suggestion du PO et les exemples donnés par tous les autres. Étant donné que la plupart des gens cherchaient à gérer les nombres négatifs, cette solution le fait sans utiliser de fonctions spéciales, telles que les abs, etc.
En évitant le module et en utilisant la division à la place, le nombre négatif est un résultat naturel, bien qu’il soit arrondi au nombre inférieur. Une fois la version arrondie calculée, le calcul requirejs est arrondi, dans le sens négatif ou positif.
Notez également qu’aucune fonction spéciale n’est utilisée pour calculer quoi que ce soit, il y a donc une petite augmentation de vitesse.
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) ); }} }
Cela donne les résultats que vous recherchez pour les entiers positifs:
#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; }
Et voici les sorties:
answer is: 100 answer is: 200 answer is: 500 answer is: 1100 answer is: 60 answer is: 90
Cela fonctionne pour moi mais n’a pas essayé de gérer les négatifs
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; }