8
votes

Pourquoi Java + = avoir un mauvais résultat et comment puis-je éviter cela?

Pourquoi Java + = code> a un mauvais résultat et comment puis-je empêcher ce problème? (Par exemple, toutes les voies indiquent avertissement code> dans l'IDE?)

J'ai essayé Eclipse & Intellij, mais ils ne montrent aucun avertissement. p>

code d'échantillon: P>

    {
        long a = 20000000000000000L;
        double b = 90.0;
        a += b;
        System.out.println(a); // 20000000000000088 NG
    }

    {
        long a = 10000000000000000L;
        double b = 90.0;
        a += b;
        System.out.println(a); // 10000000000000090 OK
    }

    {
        long a = 20000000000000000L;
        double b = 90.0;
        a += (long) b;
        System.out.println(a); // 20000000000000090 OK
    }


15 commentaires

@Sotirios Cette question n'explique pas pourquoi un nombre sans partie fractionnée n'aurait ce comportement.


@ 4castle La précision de double ne s'applique pas uniquement à sa partie fractionnée.


@Sotiriosdelimanolis qui est l'information exacte que la question ne contient pas. Cette réponse Même Recommande Structure des chiffres à des nombres entiers car "L'arithmétique entier dans le point flottant est exact".


Lecture requise : Que faut-il savoir que chaque informatique devrait connaître sur l'arithmétique de points flottants


@Sotiriosdelimanolis Je ne pense pas que la question est en double. Il peut expliquer pourquoi 0,1 + 0,2 = 0,30000000000000004 mais pas 1000000000000000000 + 90 = 10000000000000088


C'est la même question de précision. Essayez system.out.println (20000000000000090D); et vous verrez la même chose. Laissez-moi essayer de trouver un meilleur duplicata.


@Sotiriosdelimanolis et cette réponse ne veut pas expliquer Comment puis-je empêcher ce problème? (Par exemple, n'importe quand montre l'avertissement dans l'IDE?)


Bon, je vais rouvrirai.


@andyf La raison de ces deux comportements est la même. 0.1 ne peut pas être représenté exactement dans le point flottant. Ni ne peut 1000000000000000090 . La mantissa n'a pas assez de bits. Je suppose que vous pourriez affirmer que dans le cas de 0,1 , il ne pourrait jamais y avoir suffisamment de bits, mais dans le second cas, l'ajout de bits résoudrait le problème. Tous les bits ajoutent sont déplacer le problème. Il est toujours là avec des nombres de grandeur et de précision assez grande.


Cette question se pose encore et encore. C'est un duplicata, peut-être pas exactement un dup de la question référencée par @sotiriosdelimanolis, mais un DUP néanmoins. Je savoir C'est un dup d'une réponse que j'ai écrit il y a quelques années ... je vais y trouver.


@JimGarrison Merci pour votre référence. Je pense que pour la plupart des codeurs, ils ont juste besoin d'un outil (IDE, etc.) pour leur dire que cela ne codant pas comme ça. J'ai essayé Eclipse & Intellij mais les deux ne montrent aucun avertissement.


@andyf Les IDes supposent que vous comprenez le point flottant, tout comme s'ils s'attendent à ce que vous compreniez que le débordement entier est ignoré silencieusement. Les numéros de points flottants sont une idée puissante, mais ils agissent assez différemment de notre "intuition" qu'ils doivent être étudiés et compris pour ce qu'ils sont.


@JimGarrison merci pour votre réponse. Mais, comme tous les programmeurs (comme moi) ne peuvent toujours pas remarquer ce problème, j'ai trouvé un moyen simple: juste interdire + = comme règle de codage. Voir ma réponse.


C'est tout simplement idiot. Cela n'a rien à voir avec + = et tout pour faire avec le fait que le point flottant a une précision limitée. Vous aurez le même problème si vous écrivez a = A + B à la place.


@JimGarrison mais l'IDE me montrera l'erreur de compilée lorsque a = A + B .


3 Réponses :


10
votes

Selon les JLS, ceci expression d'affectation des composés xxx

est équivalente à xxx

car b est un double , Promotion numérique binaire se produit avant que l'addition ne se produise. Les deux opérandes sont convertis en valeurs double et l'addition se produit avec une arithmétique à virgule flottante.

La valeur 2000000000000000090 ne peut pas être représenté exactement comme un double et donc vous perdez la précision, obtenez 20000000000000088 à la place. Il est ensuite reconjecté à un long qui a suffisamment de précision pour représenter 20000000000000088 .

Votre deuxième extrait produit 10000000000000090 qui Peut être représenté exactement comme un double .

Votre troisième extrait est équivalent à xxx

qui utilise Entier Arithmétique . 2000000000000000090 peut être représenté sous la forme d'un long .

Je ne connais pas d'outils pour vous avertir à ce sujet.

Related:


0 commentaires

1
votes

Java utilise Numéros de points flottants IEEE 64 bits , qui utilisent 53 chiffres binaires pour représenter la important. Dans le premier exemple, xxx pré>

ici la variable A code> est converti en double qui doit utiliser un exposant supérieur à 1 pour le représenter, et ce ne sera plus Une représentation exacte de la valeur 2000000000000000000L. P>

Dans cet exemple, il est possible de représenter A code> en utilisant uniquement le signification et un exposant de 1. P>

{
    long a = 20000000000000000L;
    double b = 90.0;
    a += (long) b;
    System.out.println(a); // 20000000000000090 OK
}


0 commentaires

0
votes

Comme tout le programmeur (comme moi) peut comprendre Promotion numérique binaire ,

pour empêcher tout + = Codage manquant, j'ai trouvé un moyen simple: il suffit d'interdire tout + = comme règle de codage.

Je viens de grep tout + = dans mes projets et modifiez A + = b à A = A + B .

IDE montre des erreurs tout en tapant non correspondant lorsque + , comme ci-dessous:

 IDE Show compile des erreurs


0 commentaires