Question

Cela se sent comme le genre de code qui échoue uniquement in situ, mais je vais essayer de l'adapter dans un extrait de code qui représente ce que je vois.

float f = myFloat * myConstInt; /* Where myFloat==13.45, and myConstInt==20 */
int i = (int)f;
int i2 = (int)(myFloat * myConstInt);

Après avoir quitté à travers le code, i == 269 et i2 == 268. Que se passe ici pour expliquer la différence?

Était-ce utile?

La solution

math flotteur peut être effectué à une plus grande précision que la publicité. Mais dès que vous stockez dans float f, que la précision supplémentaire est perdue. Vous n'êtes pas perdre cette précision dans la deuxième méthode jusqu'à ce que, bien sûr, vous lancez le résultat vers le bas pour int.

Edit: Voir cette question Pourquoi diffère précision en virgule flottante en C # quand ils sont séparés par parantheses et lorsqu'ils sont séparés par des déclarations? pour une meilleure explication que je ne doute fourni.

Autres conseils

Les variables à virgule flottante ne sont pas infiniment précis. Utilisez une décimale si vous avez besoin de ce genre de précision.

modes d'arrondi peut également jouer dans cette question, mais le problème de précision est celui que vous utilisez en ici, autant que je sache.

virgule flottante a une précision limitée et est basée sur binaire plutôt que décimal. Le nombre décimal 13,45 ne peut pas être représenté exactement au point de Floating binaire, donc arrondit. La multiplication par 20 exagèrent plus la perte de précision. À ce stade, vous avez 268,999 ... - pas 269 - donc la conversion en entier tronque à 268

.

Pour arrondir à l'entier le plus proche, vous pouvez essayer d'ajouter 0,5 avant de retransférer entier.

Pour l'arithmétique « parfait », vous pouvez essayer d'utiliser un type numérique décimal ou rationnelle - Je crois que C # a des bibliothèques pour les deux, mais je ne suis pas certain. Ceux-ci seront plus lents, cependant.

EDIT - J'ai trouvé un type « décimal » jusqu'à présent, mais pas rationnel - je peux me tromper que d'être disponible. Décimal à virgule flottante est inexact, tout comme binaire, mais il est le genre d'inexactitude que nous sommes habitués, il donne des résultats moins surprenants.

Remplacer par

double f = myFloat * myConstInt;

et voir si vous obtenez la même réponse.

Je voudrais proposer une autre explication.

Voici le code, que je l'ai annotées (je regardais en mémoire pour disséquer les flotteurs):

 float myFloat = 13.45; //In binary is 1101.01110011001100110011
 int myConstInt = 20;
 float f = myFloat * myConstInt; //In binary is exactly 100001101 (269 decimal)
 int i = (int)f; // Turns float 269 into int 269 -- no surprises
 int i2 = (int)(myFloat * myConstInt);//"Extra precision" causes round to 268

Regardons de plus près les calculs:

  • f = 1101,01110011001100110011 * 10100 = 100.001.100,111111111111111 111

    La partie après l'espace est les bits 25-27 qui causent peu 24 d'être arrondi, et par conséquent la valeur entière à être arrondi jusqu'à 269

  • int i2 = (int) (myFloat * myConstInt)

    myfloat est étendue à double précision pour le calcul (0s sont annexés): 1101,0111001100110011001100000000000000000000000000000

    myfloat * 20 = 100001100.11111111111111111100000000000000000000000000

    Bits 54 et au-delà sont 0s, donc pas arrondi se fait: les résultats de la distribution du nombre entier 268

    .

    (Une explication similaire fonctionnerait si la précision étendue est utilisée.)

Mise à jour: J'affiné ma réponse et écrit un article complet appelé

scroll top