Manière concise d’implémenter round () en C?

Le C intégré que j’utilise n’a pas de fonction round (), c’est une librairie mathématique. Quelle serait une manière concise de l’implémenter en C? Je pensais l’imprimer sur une chaîne, chercher la décimale, puis trouver le premier caractère après la période, puis arrondir si> = 5, sinon, vers le bas. etc. Je me demandais s’il y avait quelque chose de plus intelligent.

Merci Fred

Vous pouvez réinventer la roue, comme le suggèrent de nombreuses autres réponses. Alternativement, vous pouvez utiliser la roue de quelqu’un d’autre – je suggérerais Newlib, qui est sous licence BSD et destinée à être utilisée sur des systèmes embarqués. Il gère correctement les nombres négatifs, les NaN, les infinis et les cas qui ne sont pas représentables sous forme de nombres entiers (en raison de leur taille trop grande), et ce, de manière efficace, en utilisant des exposants et du masquage plutôt que des opérations à virgule flottante généralement plus coûteuses. En outre, il est régulièrement testé, vous savez donc qu’il ne contient pas de bugs criants.

La source Newlib peut être un peu difficile à naviguer, alors voici les bits que vous voulez:

Version flottante: https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/sf_round.c;hb=master

Version double: https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/s_round.c;hb=master

Macros d’extraction de mots définies ici: https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/fdlibm.h;hb=master

Si vous avez besoin d’autres fichiers à partir de là, le répertoire parent est celui-ci: https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=tree;f=newlib/libm/common;hb = maître

Pour mémoire, voici le code de la version float. Comme vous pouvez le constater, un peu de complexité est nécessaire pour traiter correctement tous les cas possibles.

 float roundf(x) { int signbit; __uint32_t w; /* Most significant word, least significant word. */ int exponent_less_127; GET_FLOAT_WORD(w, x); /* Extract sign bit. */ signbit = w & 0x80000000; /* Extract exponent field. */ exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127; if (exponent_less_127 < 23) { if (exponent_less_127 < 0) { w &= 0x80000000; if (exponent_less_127 == -1) /* Result is +1.0 or -1.0. */ w |= ((__uint32_t)127 << 23); } else { unsigned int exponent_mask = 0x007fffff >> exponent_less_127; if ((w & exponent_mask) == 0) /* x has an integral value. */ return x; w += 0x00400000 >> exponent_less_127; w &= ~exponent_mask; } } else { if (exponent_less_127 == 128) /* x is NaN or infinite. */ return x + x; else return x; } SET_FLOAT_WORD(x, w); return x; } 
 int round(double x) { if (x < 0.0) return (int)(x - 0.5); else return (int)(x + 0.5); } 
 int round(float x) { return (int)(x + 0.5); } 

Avertissement: ne fonctionne que sur les nombres positifs.

IEEE 754 recommande l’approche «demi-tour à pair»: si la partie décimale de d est 0,5, arrondissez au nombre entier pair le plus proche. Le problème est que le fait d’arrondir une fraction de 0,5 dans la même direction introduit un biais dans les résultats; donc, vous devez arrondir un demi-demi la moitié du temps à la moitié, puis le bit “arrondir au nombre entier pair le plus proche”, arrondir au nombre impair le plus proche fonctionnerait également, tout comme le fait de jeter une pièce équitable pour déterminer le chemin à suivre aller.

Je pense que quelque chose de plus semblable à ceci serait IEEE-correct:

 #include  int is_even(double d) { double int_part; modf(d / 2.0, &int_part); return 2.0 * int_part == d; } double round_ieee_754(double d) { double i = floor(d); d -= i; if(d < 0.5) return i; if(d > 0.5) return i + 1.0; if(is_even(i)) return i; return i + 1.0; } 

Et celui-ci devrait être C99-ish (ce qui semble spécifier que les nombres avec des fractions de 0.5 doivent être arrondis à zéro):

 #include  double round_c99(double x) { return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5); } 

Et une version plus compacte de mon premier round_c99() , celui-ci gère mieux le franchissement de la limite de la mantisse 56 bits en ne reposant pas sur x+0.5 ou x-0.5 étant des choses sensées à faire:

 #include  double round_c99(double d) { double int_part, frac_part; frac_part = modf(d, &int_part); if(fabs(frac_part) < 0.5) return int_part; return int_part > 0.0 ? int_part + 1.0 : int_part - 1.0; } 

Cela posera des problèmes si |int_part| >> 1 |int_part| >> 1 mais arrondir un double avec un grand exposant est inutile. Je suis sûr qu’il y a NaN dans les trois aussi mais mon masochisme a des limites et la programmation numérique n’est vraiment pas mon truc.

Le calcul en virgule flottante laisse suffisamment de place à des erreurs subtiles, de sorte que la concision peut ne pas être la meilleure exigence.

Une solution encore meilleure serait de battre votre fournisseur de compilateur du visage et du cou jusqu’à ce qu’ils fournissent une bibliothèque de mathématiques appropriée.

Dans les temps anciens, lorsque l’arrondi n’était pas bien défini d’un système à l’autre, nous avions écrit une fonction d’arrondissement mise à l’échelle qui multipliait d’abord le nombre afin que l’arrondi soit effectué en tronquant le nombre.
Pour arrondir à 2 décimales, multiplier par 100, append 0,5, tronquer les résultats et diviser par 100.
Voici comment cela a été fait pour les machines-outils à commande numérique, lorsque les commandes ne pouvaient exécuter un programme CN que s’il était sur place (écrous morts).

Une façon en utilisant une opération de chaîne

 float var=1.2345; char tmp[12]={0x0}; sprintf(tmp, "%.2f", var); var=atof(tmp); 

Une autre façon en utilisant des opérations numériques

 float var=1.2345; int i=0; var*=100; i=var; var=i; var/=100; 

Pouvez-vous utiliser un entier? faire ce qui suit:

 int roundup(int m, int n) { return (m + n - 1) / n ; } int rounddown(int m, int n) { return m / n; } int round(int m, int n) { int mod = m % n; if(mod >= (n + 1) / 2) return roundup(m, n); else return rounddown(m, n); }