34 votes

Comportement étrange en multiplication décimale C #

J'ai remarqué un comportement étrange lors de la multiplication de valeurs décimales en C #. Considérons les opérations de multiplication suivantes:

 1.1111111111111111111111111111m * 1m = 1.1111111111111111111111111111 // OK
1.1111111111111111111111111111m * 2m = 2.2222222222222222222222222222 // OK
1.1111111111111111111111111111m * 3m = 3.3333333333333333333333333333 // OK
1.1111111111111111111111111111m * 4m = 4.4444444444444444444444444444 // OK
1.1111111111111111111111111111m * 5m = 5.5555555555555555555555555555 // OK
1.1111111111111111111111111111m * 6m = 6.6666666666666666666666666666 // OK
1.1111111111111111111111111111m * 7m = 7.7777777777777777777777777777 // OK
1.1111111111111111111111111111m * 8m = 8.888888888888888888888888889  // Why not 8.8888888888888888888888888888 ?
1.1111111111111111111111111111m * 9m = 10.000000000000000000000000000 // Why not 9.9999999999999999999999999999 ?
 

Ce que je ne peux pas comprendre, ce sont les deux derniers des cas ci-dessus. Comment est-ce possible?

73voto

Jon Skeet Points 692016

decimal magasins de 28 ou 29 de chiffres significatifs (96 bits). Fondamentalement, la mantisse est dans la plage de -/+ 79,228,162,514,264,337,593,543,950,335.

Cela signifie que jusqu'à environ 7.9.... vous pouvez obtenir 29 chiffres significatifs avec précision mais avant que vous ne le pouvez pas. C'est pourquoi à la fois le 8 et le 9 va mal, mais pas les valeurs précédemment. Vous ne devez compter sur 28 chiffres significatifs en général, pour éviter les impairs des situations de ce genre.

Une fois que vous réduire votre entrée à 28 chiffres significatifs, vous aurez le résultat que vous attendez:

using System;

class Test
{
    static void Main()
    {
        var input = 1.111111111111111111111111111m;
        for (int i = 1; i < 10; i++)
        {
            decimal output = input * (decimal) i;
            Console.WriteLine(output);
        }
    }
}

2voto

Fred Mitchell Points 1068

Les mathématiciens distinguer entre les nombres rationnels et le sur-ensemble des nombres réels. Les opérations arithmétiques sur les nombres rationnels est bien défini et précis. De l'arithmétique (en utilisant les opérateurs d'addition, de soustraction, multiplication et division) sur les nombres réels est "précis" que dans la mesure où soit les nombres irrationnels sont laissées dans un irrationnel forme (symbolique) ou éventuellement convertible dans quelques expressions d'un nombre rationnel. Exemple, la racine carrée des deux n'a pas de virgule (ou de toute autre base rationnelle) de la représentation. Cependant, la racine carrée de deux, multiplié par la racine carrée de deux qui est rationnel - 2, évidemment.

Les ordinateurs et les langues en cours d'exécution sur eux, généralement de mettre en œuvre uniquement les nombres rationnels cachés derrière des noms tels que int, long int, float, double précision, réel (FORTRAN), ou un autre nom qui suggère des nombres réels. Mais les nombres rationnels sont inclus limitée, à la différence de l'ensemble des nombres rationnels dont la portée est infinie.

Exemple Trivial - pas trouvés sur les ordinateurs. 1/2 * 1/2 = 1/4 Qui fonctionne très bien si vous avez une classe de nombres Rationnels ET de la taille des numérateurs et des dénominateurs ne pas dépasser les limites de l'arithmétique des nombres entiers. donc (1,2) * (1,2) -> (1,4)

Mais si les nombres rationnels disponibles ont été décimal ET limitée à un seul chiffre après la virgule - difficile, mais représentant du choix au moment de choisir une mise en œuvre pour l'approximation rationnelle (float/réel, etc.) les chiffres, puis 1/2 serait parfaitement convertibles à 0.5, 0.5 + 0.5 serait égal à 1.0, mais 0.5 * 0.5 devrait être de 0,2 ou 0,3!

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