Blog
Float est maléfique
La représentation en base 2 des nombres réels est pour moi un artefact de l'époque où chaque bit et chaque flop comptaient, mais je dois admettre qu'avec la mémoire et la puissance de calcul dont nous disposons aujourd'hui, je ne comprends pas pourquoi les langages modernes n'utilisent pas la représentation en base 10 par défaut.
Laisse-moi t'expliquer. Il est important de se rappeler qu'un nombre avec une représentation en base finie 10 n'a pas nécessairement une représentation en base finie 2, et inversement. Bien entendu, ce problème n'est pas inhérent à la base 2 mais le fait est que, dans la vie de tous les jours, sauf pour certains geeks, nous calculons en base 10 : nous nous attendons à ce que 1/3 ait un nombre infini de chiffres après la virgule, et non 12/10. À cet égard, la base 2 peut parfois donner des résultats surprenants. Pour vous en convaincre, tapez simplement la ligne suivante dans votre console Ruby :
1,2 à 1,0 = 0,2
Pour la plupart des calculs, les erreurs sont peut-être suffisamment petites pour être ignorées, mais il existe toujours un risque qu'elles s'additionnent. Si votre application gère de l'argent d'une manière ou d'une autre, cela peut devenir un réel problème (les comptables n'aiment pas que les centimes apparaissent ou disparaissent sans raison).
La moins bonne idée : travailler avec des cents
Je lis souvent que pour éviter les erreurs de conversion de base lorsqu'il s'agit d'argent, vous devez travailler en cents, afin d'utiliser uniquement des entiers. Cette solution peut sembler simple et élégante, mais elle ne fonctionne tout simplement pas pour deux raisons simples.
Tout d'abord, le cent n'est pas toujours la plus petite unité monétaire : il suffit de penser à une application traitant des prix des pompes à essence ou des taux de change. Ensuite, même si c'est le cas dans votre application, ou si vous travaillez avec des nanocents, vous finirez par obtenir des nombres réels : divisez 12 cents par 10 (par exemple pour calculer une moyenne) et vous reviendrez à la case départ.
Représentation décimale dans votre code et dans la base de données
Ruby et SQL vous permettent de stocker des nombres dans leur représentation décimale, en utilisant respectivement le type de champ BigDecimal et le type de champ decimal (parfois aussi appelé numérique). Dans les coulisses, Float et BigDecimal utilisent tous deux une représentation en virgule flottante de la forme significant_digits x base^exp ; la différence étant que la base est 2 dans le premier cas et 10 dans le second.
En utilisant BigDecimal, vos calculs renverront donc toujours ce que l'être humain attend. De plus, vous pouvez obtenir un contrôle total sur vos opérations mathématiques en spécifiant la précision (nombre de chiffres significatifs), l'échelle (chiffres après le point) et la stratégie d'arrondissement, afin de ne jamais obtenir de résultats inattendus. BigDecimal a également la capacité de représenter des nombres spéciaux tels que Infinity ou NaN.
Côté base de données, utilisez simplement le type décimal dans votre migration et spécifiez une précision et une échelle (les valeurs par défaut peuvent différer), ActiveRecord s'occupera du reste. Mieux encore : les données des champs décimaux seront automatiquement récupérées dans les objets BigDecimal.
En savoir plus :