OK - je suis presque gêné de poster ceci ici (et je le supprimerai si quelqu'un vote pour fermer) car cela semble être une question fondamentale.
Est-ce la bonne façon d'arrondir un multiple d'un nombre en C++?
Je sais qu'il existe d'autres questions liées à cela, mais je suis particulièrement intéressé de savoir quelle est la meilleure façon de procéder 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 probablement pas précisé l'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 les nombres positifs, pas sûr de négatif. Il utilise uniquement 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 entendez un résultat 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;
}
Si multiple
est une puissance de 2
int roundUp(int numToRound, int multiple)
{
assert(multiple && ((multiple & (multiple - 1)) == 0));
return (numToRound + multiple - 1) & -multiple;
}
Cela fonctionne lorsque le facteur sera toujours positif:
int round_up(int num, int factor)
{
return num + factor - 1 - (num - 1) % factor;
}
Edit: Ceci retourne round_up(0,100)=100
. Veuillez voir le commentaire de Paul ci-dessous pour une solution qui retourne round_up(0,100)=0
.
C’est une généralisation du problème "comment savoir combien d’octets n bits prendront? (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
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 numéro ou base de flottant (par exemple, vous pouvez arrondir -4 à 6,75 près). Essentiellement, il convertit en point fixe, y arrondit, puis reconvertit. Il gère les négatifs en arrondissant AWAY à partir de 0. Il traite également un arrondi négatif à valeur en tournant essentiellement la fonction en roundDown.
Une version spécifique à int ressemble à ceci:
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 d’entrée négatif ajouté.
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 ça aide quelqu'un. :)
Il s’agit de l’approche c ++ moderne utilisant une fonction de modèle qui fonctionne pour float, double, long, int et short (mais pas pour longtemps, et long double en raison des valeurs doubles utilisées).
#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;
}
Mais vous pouvez facilement ajouter un support pour long long
et long double
avec une spécialisation de modèle, comme indiqué ci-dessous:
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));
}
Pour créer des fonctions à arrondir, utilisez std::ceil
et pour toujours arrondir, utilisez std::floor
. Mon exemple ci-dessus est arrondi avec std::round
.
Créez la fonction de modèle "arrondir" ou mieux connue sous le nom de "plafond arrondi" comme indiqué ci-dessous:
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));
}
Créez la fonction de modèle "arrondir vers le bas" ou mieux connue sous le nom de "plancher arrondi" comme indiqué ci-dessous:
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));
}
Tout d’abord, votre condition d’erreur (multiple == 0) devrait probablement avoir une valeur de retour. Quoi? Je ne sais pas. Peut-être que vous voulez lancer une exception, à vous de choisir. Mais ne rien retourner est dangereux.
Deuxièmement, vous devriez vérifier que numToRound n'est pas déjà un multiple. Sinon, lorsque vous ajoutez multiple
à roundDown
, vous obtiendrez la mauvaise réponse.
Troisièmement, vos moulages sont incorrects. Vous transformez numToRound
en un entier, mais c'est déjà un entier. Vous devez utiliser cast pour doubler avant la division, puis pour int après la multiplication.
Enfin, que voulez-vous pour les nombres négatifs? Arrondir "en haut" peut signifier arrondir à zéro (arrondir dans la même direction que les nombres positifs) ou s'éloigner de zéro (nombre négatif "plus grand"). Ou peut-être que vous ne vous en souciez pas.
Voici une version avec les trois premiers correctifs, mais je ne traite pas le 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 j'ai fini 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 saisi restera le même s'il s'agit déjà d'un multiple.
Voici la sortie x86_64 que GCC génère 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’Assemblée: http://goo.gl/DZigfX
Chacune de ces instructions est extrêmement rapide, la fonction est donc extrêmement rapide. Étant donné que le code est si petit et rapide, il peut être utile de inline
la fonction lors de son utilisation.
Crédit:
J'utilise:
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
}
et pour les pouvoirs de deux:
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)
}
Notez que ces deux valeurs arrondissent les valeurs négatives vers zéro (c'est-à-dire arrondies à l'infini positif pour toutes les valeurs), aucune d'elles ne repose sur le dépassement de capacité 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
Pour toujours arrondir
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
Pour 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 arrondir 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
peut être cela peut aider:
int RoundUpToNearestMultOfNumber(int val, int num)
{
assert(0 != num);
return (floor((val + num) / num) * num);
}
Il est probablement plus sûr de lancer les flottants et d'utiliser ceil () - à moins que vous ne 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 son 1,5, ce sera 2) mais 1,49 sera 1,99 donc 1.
EDIT - Désolé de ne pas voir que vous vouliez arrondir, je suggérerais 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;
Voici ce que je ferais:
#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;
}
Le code n'est peut-être pas optimal, mais je préfère un code propre à une performance sèche.
int roundUp (int numToRound, int multiple)
{
return multiple * ((numToRound + multiple - 1) / multiple);
}
bien que:
suggérerait plutôt d'utiliser des entiers non signés, qui ont un comportement de débordement défini.
Vous aurez une exception est multiple == 0, mais ce n'est pas un problème bien défini dans ce cas.
J'utilise une combinaison de module pour annuler l'ajout du reste 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 reste puis modulons cela avec le diviseur à nouveau pour l'annuler si c'est le diviseur lui-même alors ajoutez x
.
round_up(19, 3) = 21
J'ai trouvé un algorithme qui ressemble un peu à celui posté ci-dessus:
int [(| x | + n-1)/n] * [(nx)/| x |], où x est une valeur entrée par l'utilisateur et n est le multiple utilisé.
Cela fonctionne pour toutes les valeurs x, où x est un entier (positif ou négatif, y compris zéro). Je l'ai écrit spécialement pour un programme C++, mais cela peut être implémenté dans n'importe quel langage.
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 utilisateurs cherchaient à gérer les nombres négatifs, cette solution ne sert à rien, sans utilisation de fonctions spéciales, telles que abs, etc.
En évitant le module et en utilisant la division, le nombre négatif est un résultat naturel, bien qu’il soit arrondi. Une fois que la version arrondie est calculée, elle effectue les calculs nécessaires pour arrondir, 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;
}
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} ))
}
Pour numToRound négatif:
Cela devrait être très facile à faire, mais l'opérateur modulo% standard 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 renvoie 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);
}
Arrondis au multiple le plus proche qui correspond à une puissance de 2
unsigned int round(unsigned int value, unsigned int multiple){
return ((value-1u) & ~(multiple-1u)) + multiple;
}
Cela peut être utile pour l'allocation le long de lignes de cache, où l'incrément d'arrondi souhaité est une puissance de deux, mais la valeur résultante ne doit en être qu'un multiple. Sur gcc
, le corps de cette fonction génère 8 instructions d'assemblage sans division ni branche.
round( 0, 16) -> 0
round( 1, 16) -> 16
round( 16, 16) -> 16
round(257, 128) -> 384 (128 * 3)
round(333, 2) -> 334
Vous obtenez les résultats que vous recherchez pour les entiers positifs:
#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;
}
Et voici les sorties:
answer is: 100
answer is: 200
answer is: 500
answer is: 1100
answer is: 60
answer is: 90
Je pense que cela devrait vous aider. J'ai écrit le programme ci-dessous en C.
# 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;
}
/// 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) );
}}
}