arrondi d'un double en entier (nearbyint vs round)
Le
Isidor Ducasse
Bonjour,
Excusez si le sujet a déjà été posté, je ne l'ai pas trouvé dan=
s les
recherches.
Excusez, l'exemple est en C++, mais je pense que vous arriverez
facilement à vous y retrouvez
Voilà mon pb, je ne comprends pas nearbyint, rint, round de la lib
math
round semble convenir à mon besoin, mais qu'est-ce que c'est que cet
arrondi à l'entier impair le plus proche?
A quelle correspondance mathématique ou convention, nearbyint et rint
font-elles référence??
le code de test suivi du résultat:
<code>
#include <cstdio>
#include <cstdlib>
#include <cmath>
void testRound(double dbl)
{
double dCeil = ceil(dbl);
double dFloor = floor(dbl);
long lMano = (long) (dbl + 5e-1);
long lNear = (long) nearbyint(dbl);
long lRint = (long) rint(dbl);
long lRound = (long) round(dbl);
double diffCeil = dCeil-dbl;
double diffFloor = dbl-dFloor;
long lMano2 = (long)(
diffCeil == diffFloor ? dCeil : // explicite la convention:
si xxx.5 alors on arrondi vers ceil
diffCeil > diffFloor ? dFloor : dCeil
);
printf("| %.19f | %5.0f | %5.0f | %13.0f | %13ld | %13ld | %13ld |
%13ld | %13ld |",
dbl, dFloor, dCeil, dbl, lMano, lNear, lRint, lRound,
lMano2);
}
int main(int argc, const char** argv)
{
double dblArray[] = { 5e-1, 1.+5e-1, 2.+5e-1 }; // 0.5 1.5 2.5
int sizeA = sizeof(dblArray) / sizeof(double);
printf("| %-21s | %-5s | %-5s | %-13s | %-13s | %-13s | %-13s |
%-13s | %-13s |",
"tested value", "floor", "ceil", "print(\"%.0f\")", "(int)(x
+0.5)", "nearbyint", "rint", "round", "ceil flr near");
for (int i = 0; i<sizeA; i++)
{
testRound( dblArray[i] );
}
return EXIT_SUCCESS;
}
</code>
<code>
| tested value | floor | ceil | print("%.0f") | (int)(x
+0.5) | nearbyint | rint | round | ceil flr near
|
| 0.5000000000000000000 | 0 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
| 1.5000000000000000000 | 1 | 2 | 2 |
2 | 2 | 2 | 2 | 2 |
| 2.5000000000000000000 | 2 | 3 | 2 |
3 | 2 | 2 | 3 | 3 |
</code>
Excusez si le sujet a déjà été posté, je ne l'ai pas trouvé dan=
s les
recherches.
Excusez, l'exemple est en C++, mais je pense que vous arriverez
facilement à vous y retrouvez
Voilà mon pb, je ne comprends pas nearbyint, rint, round de la lib
math
round semble convenir à mon besoin, mais qu'est-ce que c'est que cet
arrondi à l'entier impair le plus proche?
A quelle correspondance mathématique ou convention, nearbyint et rint
font-elles référence??
le code de test suivi du résultat:
<code>
#include <cstdio>
#include <cstdlib>
#include <cmath>
void testRound(double dbl)
{
double dCeil = ceil(dbl);
double dFloor = floor(dbl);
long lMano = (long) (dbl + 5e-1);
long lNear = (long) nearbyint(dbl);
long lRint = (long) rint(dbl);
long lRound = (long) round(dbl);
double diffCeil = dCeil-dbl;
double diffFloor = dbl-dFloor;
long lMano2 = (long)(
diffCeil == diffFloor ? dCeil : // explicite la convention:
si xxx.5 alors on arrondi vers ceil
diffCeil > diffFloor ? dFloor : dCeil
);
printf("| %.19f | %5.0f | %5.0f | %13.0f | %13ld | %13ld | %13ld |
%13ld | %13ld |",
dbl, dFloor, dCeil, dbl, lMano, lNear, lRint, lRound,
lMano2);
}
int main(int argc, const char** argv)
{
double dblArray[] = { 5e-1, 1.+5e-1, 2.+5e-1 }; // 0.5 1.5 2.5
int sizeA = sizeof(dblArray) / sizeof(double);
printf("| %-21s | %-5s | %-5s | %-13s | %-13s | %-13s | %-13s |
%-13s | %-13s |",
"tested value", "floor", "ceil", "print(\"%.0f\")", "(int)(x
+0.5)", "nearbyint", "rint", "round", "ceil flr near");
for (int i = 0; i<sizeA; i++)
{
testRound( dblArray[i] );
}
return EXIT_SUCCESS;
}
</code>
<code>
| tested value | floor | ceil | print("%.0f") | (int)(x
+0.5) | nearbyint | rint | round | ceil flr near
|
| 0.5000000000000000000 | 0 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
| 1.5000000000000000000 | 1 | 2 | 2 |
2 | 2 | 2 | 2 | 2 |
| 2.5000000000000000000 | 2 | 3 | 2 |
3 | 2 | 2 | 3 | 3 |
</code>

Poser une question


Les deux premiers sont identiques en dehors du traitement des exceptions
de résultat inexact (sujet que je ne vais pas développer, si tu ne vois
pas de quoi il retourne c'est que la différence ne t'importe pas).
round() utilise un algorithme spécifique (c'est l'algorithme que l'on
nous apprend à l'école, prendre la valeur absolue la plus grande) dans
le cas des valeurs xxx.5, indépendament des spécifications de fesetround().
Il s'agit d'un autre algorithme (souvent appelé « arrondi du banquier »,
ou plus exactement son contraire), qui à ma connaissance n'est pas
_requis_ dans une implémentation de la norme C.
L'arrondi à l'entier *pair* le plus proche (« l'arrondi du banquier »
proprement dit) est requis comme méthode par défaut par la norme IEEE754
(qui est probablement la référence utilisée par ta machine).
L'intérêt de cet algorithme par rapport à celui de round() est que les
erreurs sont statistiquement mieux réparties.
Suivant la valeur de fegetround():
FE_DOWNWARD comme floor()
FE_TONEAREST arrondi classique au plus proche
FE_TOWARDZERO arrondi par défaut, comme trunc(),
c'est l'effet habituel de la conversion (int)X
FE_UPWARD comme ceil()
On remarquera que (à ma connaissance) l'effet de FE_TONEAREST sur les
valeurs xxx.5 n'est pas spécifié par la norme C, on peut récupérer xxx
ou (xxx+1) suivant les implémentations; une possibilité est d'avoir
l'effet de round(), une autre serait d'arrondir vers la valeur absolue
la plus petite, on peut aussi avoir un arrondi par excès ou par défaut,
un arrondi du banquier (à l'entier pair le plus proche) ou son
contraire, voire le choix au hasard entre les deux valeurs.
... ne concerne que ta machine.
Antoine
Cependant, si __STDC_IEC_559__ est défini, on doit avoir la règle
de l'arrondi pair, comme l'exige la norme IEC 60559 (qui reprend
l'ancienne norme IEEE 754-1985).
--
Vincent Lefèvre 100% accessible validated (X)HTML - Blog: Work: CR INRIA - computer arithmetic / Arénaire project (LIP, ENS-Lyon)