95 votes

C - Arrondir la division entière (au lieu de tronquer)

J'étais curieux de savoir comment arrondir un nombre au dixième le plus proche. Par exemple, si j'avais:

 int a = 59 / 4;
 

qui serait 14,75 calculé en virgule flottante; Comment puis-je stocker le nombre comme 15 dans "a"?

158voto

Jonathan Leffler Points 299946

L'idiome standard pour arrondir les nombres entiers est le suivant:

 int a = (59 + (4 - 1)) / 4;
 

Vous ajoutez le diviseur moins un au dividende.

63voto

ericbn Points 743

Un code qui fonctionne pour tout signe en dividende et diviseur.

 int divRoundClosest(const int n, const int d)
{
  return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d);
}
 

La macro du noyau Linux DIV_ROUND_CLOSEST ne fonctionne pas pour les diviseurs négatifs!

56voto

0xC0DEFACE Points 2212
 int a = 59.0f / 4.0f + 0.5f;
 

Cela ne fonctionne que lors de l'affectation à un int car il rejette quoi que ce soit après le '.'

Modifier: Cette solution ne fonctionnera que dans les cas les plus simples. Une solution plus robuste serait:

 unsigned int round_div(unsigned int dividend, unsigned int divisor)
{
    return (dividend + (divisor / 2)) / divisor;
}
 

22voto

WayneJ Points 257

La réponse ci-dessus est techniquement correcte mais débordera prématurément. Vous devriez plutôt utiliser quelque chose comme ceci:

 int a = (59 - 1)/ 4 + 1;
 

Je suppose que vous essayez vraiment de faire quelque chose de plus général:

 int divide(x, y)
{
   int a = (x -1)/y +1;

   return a;
}
 

x + (y-1) est susceptible de déborder, donnant un résultat incorrect; alors que, x - 1 ne sera submergé que si x = min_int ...

15voto

WayneJ Points 257

(Édité) L'arrondissement des nombres entiers avec virgule flottante est la méthode la plus simple solution de ce problème; cependant, selon le problème est peut-être possible. Par exemple, dans les systèmes embarqués virgule flottante, la solution est peut-être trop coûteux.

Cette opération à l'aide de math entier s'avère être un peu dur et un peu de peu intuitive. Le premier posté solution a bien fonctionné pour les le problème que j'avais utilisé pour, mais après avoir qualifié les résultats sur la plage des entiers, il s'est avéré être très mauvais en général. En regardant à travers plusieurs livres sur le bit se tourner et intégré de mathématiques de retour peu de résultats. Un couple de notes. Tout d'abord, j'ai testé uniquement pour des entiers positifs, mon travail n'implique pas négatif numérateurs ou les dénominateurs. Deuxièmement, et test exhaustif de 32 bits entiers est de calcul prohibitif, j'ai donc commencé avec 8 bits entiers puis made sûr que j'ai obtenu des résultats similaires avec 16 bits entiers.

J'ai commencé avec les 2 solutions que j'avais déjà proposé:

#define DIVIDE_WITH_ROUND(N, D) (((N) == 0) ? 0:(((N * 10)/D) + 5)/10)

#define DIVIDE_WITH_ROUND(N, D) (N == 0) ? 0:(N - D/2)/D + 1;

Ma pensée était que la première version de débordement avec de gros chiffres et de la deuxième underflow avec de petits nombres. Je n'ai pas pris 2 choses en considération. 1.) le 2ème problème est en fait récursive puisque pour obtenir la bonne réponse, vous avez de bien ronde D/2. 2.) Dans le premier cas, vous avez souvent débordement et puis underflow, les deux annulant les uns les autres. Ici est une erreur de l'intrigue des deux (mauvaises) des algorithmes:Divide with Round1 8 bit x=numerator y=denominator

Cette courbe montre que le premier algorithme est seulement mauvaise pour les petits dénominateurs (0 < d < 10). De façon inattendue, il gère en fait de grandes numérateurs mieux que la 2ème version.

Voici le tracé de la 2ème algorithme: 8 bit signed numbers 2nd algorithm.

Comme prévu, il ne parvient pas pour les petites numérateurs mais échoue également pour les plus grands numérateurs que la 1ère version.

C'est clairement le meilleur point de départ pour une version correcte:

#define DIVIDE_WITH_ROUND(N, D) (((N) == 0) ? 0:(((N * 10)/D) + 5)/10)

Si votre dénominateurs est > 10 alors cela fonctionnera correctement.

Un cas spécial est nécessaire pour D == 1, il suffit de retourner N. Un cas spécial est nécessaire pour D== 2, = N/2 + (N 1) // Round up, s'il est impair.

D >= 3 a également des problèmes une fois N devient assez grand. Il s'avère que de plus grands dénominateurs n'ont que des problèmes avec les plus grandes numérateurs. Pour 8 bits signé nombre le problème des points sont

if (D == 3) && (N > 75))

(retour D/N pour ces)

Donc, en général, la pointe où le numérateur est mauvaise est quelque part autour de

Ce n'est pas exact, mais à proximité. Lorsque vous travaillez avec 16 bits ou plus grand nombre l'erreur < 1% si vous venez de faire un C diviser (troncature) pour ces cas.

Pour 16 bits des nombres signés les tests seraient

else if ((D == 4) && (N > 100))

Bien sûr, pour des entiers non signés MAX_INT serait remplacé par MAX_UINT. Je suis sûr qu'il y est une formule exacte pour la détermination de la plus grande N qui va travailler pour un particulier D et le nombre de bits mais je n'ai pas plus de temps pour travailler sur ce problème...

(J'ai l'impression de rater ce graphique pour le moment, je vais modifier et ajouter plus tard.) C'est un graphique de l'8 bits version avec les cas particuliers mentionné ci-dessus:![8 bits signés avec des cas particuliers pour
3

Notez que pour les 8 bits de l'erreur est de 10% ou moins pour toutes les erreurs dans le graphique 16 bits, est < 0.1%.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X