web-dev-qa-db-fra.com

Pourquoi beaucoup de programmes (anciens) utilisent-ils plancher (0,5 + entrée) au lieu de rond (entrée)?

Les différences résident dans la valeur renvoyée donnant des entrées autour de la rupture, je crois, telle que ce code :

int main()
{
    std::cout.precision(100);

    double input = std::nextafter(0.05, 0.0) / 0.1;
    double x1 = floor(0.5 + input);
    double x2 = round(input);

    std::cout << x1 << std::endl;
    std::cout << x2 << std::endl;
}

qui produit:

1
0

Mais ce ne sont que des résultats différents au final, on choisit son préféré. Je vois beaucoup de "vieux" programmes C/C++ utilisant floor(0.5 + input) au lieu de round(input).

Y a-t-il une raison historique? Le moins cher sur le CPU?

80
markzzz

std::round est introduit en C++ 11. Avant cela, seul std::floor était disponible, donc les programmeurs l'utilisaient.

114
haccks

Il n'y a aucune raison historique que ce soit. Ce genre de déviance a été autour depuis l'année dot. Les gens le font quand ils se sentent vraiment très méchants. C'est un grave abus d'arithmétique en virgule flottante et de nombreux programmeurs professionnels expérimentés craignent. Même les Java bods l'ont fait jusqu'à la version 1.7. Gars marrants.

Ma conjecture est qu’une fonction d’arrondi allemande prête à l’emploi n’est pas officiellement disponible avant le C++ 11 (bien que C l’ait obtenu en C99), mais ce n’est vraiment pas une excuse pour adopter la prétendue alternative.

Voici la chose: floor(0.5 + input) ne récupère pas toujours le même résultat que l'appel std::round correspondant!

La raison est assez subtile: la limite pour un arrondi allemand, a.5 pour un entier a est, par une propriété fortuite de l'univers, un rationnel dyadique. Comme cela peut être représenté exactement dans une virgule flottante IEEE754 jusqu'à la 52e puissance de 2, puis que l'arrondi est une opération sans issue de toute façon, std::round fonctionne toujours correctement. Pour les autres schémas à virgule flottante, consultez la documentation.

Mais ajouter 0.5 à double peut introduire une imprécision entraînant une légère sous-exécution ou un dépassement de certaines valeurs. Si vous y réfléchissez, il est inévitable que vous ajoutiez deux valeurs double, qui sont à l'origine de conversions par déni involontaires, et que vous appliquiez une fonction qui est une fonction très puissante de l'entrée (telle qu'une fonction d'arrondi). en larmes.

Ne le faites pas .

Référence: Pourquoi Math.round (0.49999999999999994) renvoie-t-il 1

18
Bathsheba

Je pense que c'est là que vous vous trompez:

Mais ce ne sont que des résultats différents au final, on choisit son préféré. Je vois beaucoup de "vieux" programmes C/C++ utilisant floor (0.5 + entrée) au lieu de round (entrée).

Ce n'est pas le cas. Vous devez sélectionner le bon schéma d'arrondi pour le domaine. Dans une application financière, vous arrondirez en utilisant les règles de banquier (sans utiliser float en passant). Cependant, lors de l'échantillonnage, arrondir la valeur supérieure avec static_cast<int>(floor(f + .5)) génère moins de bruit d'échantillonnage, la plage dynamique est incrémentée. Lors de l’alignement des pixels, c’est-à-dire que la conversion d’une position en coordonnées d’écran, en utilisant toute autre méthode d’arrondi, produira des trous, des espaces et d’autres artefacts.

9
Michaël Roy

Une raison simple pourrait être qu’il existe différentes méthodes pour arrondir les nombres. Ainsi, à moins de connaître la méthode utilisée, vous pouvez obtenir des résultats différents.

Avec floor (), vous pouvez être cohérent avec les résultats. Si le flottant est égal ou supérieur à 0,5, son ajout se répercutera sur le prochain int. Mais .49999 laissera simplement tomber la décimale.

4
MivaScott

De nombreux programmeurs adaptent les idiomes qu'ils ont appris lors de la programmation avec d'autres langages. Toutes les langues ne disposent pas de la fonction round(), et dans ces langues, il est normal d'utiliser floor(x + 0.5) comme substitut. Lorsque ces programmeurs commencent à utiliser le C++, ils ne réalisent pas toujours qu'il existe un round() intégré, ils continuent à utiliser le style auquel ils étaient habitués.

En d'autres termes, ce n'est pas parce que vous voyez beaucoup de code que quelque chose fait quelque chose qu'il y a une bonne raison de le faire. Vous pouvez en trouver des exemples dans tous les langages de programmation. Rappelez-vous loi de l'esturgeon :

quatre vingt dix pour cent de tout est de la merde

2
Barmar