3 votes

Est-il possible qu'un nombre représenté exactement en float ne puisse PAS être représenté exactement en double ?

J'ai une question qui découle d'une autre question sur la précision des nombres flottants.

Je sais que les nombres flottants ne peuvent pas toujours être représentés avec précision et qu'ils sont donc stockés sous la forme du nombre flottant le plus proche possible.

Ma question porte en fait sur la différence de représentation de float y double .

D'où vient cette question ?

Supposons que je le fasse :

System.out.println(.475d+.075d);

alors la sortie ne serait pas 0.55 mais 0.549999 (sur ma machine)

Cependant, quand je le fais :

System.out.println(.475f+.075f);

J'obtiens la bonne réponse, c'est-à-dire 0.55 (un peu inattendu pour moi)

Jusqu'à présent, j'avais l'impression que double a plus de précision( double sera plus précis jusqu'à un plus grand nombre de décimales. ) que float . Ainsi, si un double ne peut être représenté avec précision, sa représentation flottante équivalente sera également stockée de manière imprécise.

Cependant, les résultats que j'ai obtenus sont un peu troublants pour moi. Je suis confus si :

  1. J'ai une mauvaise compréhension de ce que precision signifie ?
  2. float y double sont représentés différemment, hormis le fait que le double a plus de bits ?

8voto

Piotr Findeisen Points 1712

Un nombre que l'on peut représenter en tant que float peut être représenté comme double aussi.

Ce que vous lisez est juste formaté la sortie, vous ne lisez pas la représentation binaire réelle.

System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(.475d + .075d)));
// 11111111100001100110011001100110011001100110011001100110011001
System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(.475f + .075f)));
// 111111000011001100110011001101

double d = .475d + .075d;
System.out.println(d);
// 0.5499999999999999
System.out.println((float)d);
// 0.55 (as expected)
System.out.println((double)(float)d);
// 0.550000011920929

System.out.println( .475f + .075f == 0.550000011920929d);
// true

5voto

Jim Garrison Points 39523

La précision signifie simplement plus de bits. Un nombre qui ne peut pas être représenté comme un float mai ont une représentation exacte en tant que double mais que le nombre de ces cas est infiniment petit par rapport au nombre total de cas possibles.

Pour les cas simples comme 0.1 qui n'est pas représentable comme un nombre à virgule flottante de longueur fixe, quel que soit le nombre de bits disponibles. Cela revient à dire qu'une fraction telle que 1/7 ne peut être représentée exactement en décimal, quel que soit le nombre de chiffres que vous êtes autorisé à utiliser (tant que le nombre de chiffres est fini). Vous pouvez l'exprimer approximativement sous la forme 0,142857142857142857... en répétant encore et encore, mais vous ne serez jamais en mesure de l'écrire EXACTEMENT, quel que soit le temps que vous y consacrez.

Inversement, si un nombre est représentable exactement comme un float il sera aussi représentable exactement comme un double . Un double a un exposant plus grand et plus de bits de mantisse.

Pour votre exemple, la cause de la divergence apparente est qu'en float En augmentant la précision disponible, la différence entre 0,475 et sa représentation flottante était dans la "bonne" direction, de sorte que lorsque la troncature se produisait, elle se déroulait comme vous l'attendiez. En augmentant la précision disponible, la représentation était "plus proche" de 0,475 mais maintenant du côté opposé. À titre d'exemple brut, disons que le flottant le plus proche possible était 0,475006 mais que dans un double, la valeur la plus proche possible était 0,474999. Cela vous donnerait les résultats que vous voyez.

Edit : Voici les résultats d'une expérience rapide :

public class Test {

    public static void main(String[] args)
    {
        float  f = 0.475f;
        double d = 0.475d;

        System.out.printf("%20.16f", f);
        System.out.printf("%20.16f", d);
    }
}

Sortie :

  0.4749999940395355  0.4750000000000000

Cela signifie que la représentation en virgule flottante du nombre 0,475, si vous disposiez d'un grand nombre de bits, serait juste un tout petit peu inférieure à 0,475. C'est ce que l'on voit dans la représentation double. Toutefois, le premier bit "erroné" se trouve si loin à droite que, lorsqu'il est tronqué pour tenir dans un format float il se trouve que le résultat est de 0,475. C'est un pur accident.

1voto

supercat Points 25534

Si l'on considère que les types à virgule flottante représentent en fait des plages de valeurs, plutôt que des valeurs discrètes (par ex. 0.1f ne représente pas 13421773/134217728, mais plutôt "quelque chose entre 13421772.5/134217728 et 13421773.5/134217728"), les conversions de double a float seront généralement exactes, tandis que les conversions de float a double ne le fera généralement pas. Malheureusement, Java permet aux conversions généralement inexactes d'être effectuées implicitement, tout en exigeant un typecast dans la direction généralement exacte.

Pour chaque valeur de type float il existe une valeur de type double dont l'étendue est centrée sur le centre de l'objet de l'enquête. float La gamme de produits de l'entreprise. Cela ne signifie pas que le double est une représentation exacte de la valeur du flottant. Par exemple, la conversion de 0.1f a double donne une valeur signifiant "quelque chose entre 13421772.9999999/134217728 et 13421773.0000001/134217728", une valeur qui est décalée de plus d'un million de fois la tolérance implicite.

Pour presque toutes les valeurs de type double il existe une valeur de type float dont l'étendue inclut complètement l'étendue impliquée par l'élément double . Les seules exceptions sont les valeurs dont l'intervalle est centré précisément sur la limite entre deux valeurs de l'indice. float valeurs. La conversion de ces valeurs en float exigerait que le système choisisse une fourchette ou l'autre ; si le système arrondit vers le haut lorsque le double représentait en fait un nombre inférieur au centre de son intervalle, ou vice versa, l'intervalle du float n'engloberait pas totalement celle de la double . En pratique, cependant, ce n'est pas un problème, puisque cela signifie qu'au lieu d'une float à partir d'un double représentant un intervalle comme (13421772,5/134217728 à 13421773,5/134217728), il représenterait un intervalle comme (13421772,4999999/134217728 à 13421773,5000001/134217728). Par rapport à l'horrible imprécision résultant d'un float a double cast, cette petite imprécision n'est rien.

BTW, pour en revenir aux chiffres particuliers que vous utilisez, lorsque vous effectuez vos calculs en tant que flottant, les calculs sont les suivants :

0.075f = 20132660±½ / 268435456
0.475f = 31876710±½ /  67108864
Sum    = 18454938±½ /  33554432

En d'autres termes, la somme représente un nombre situé quelque part entre 0,54999999701 et 0,55000002682 environ. La représentation la plus naturelle est 0,55 (puisque la valeur réelle peut être supérieure ou inférieure à cette valeur, les chiffres supplémentaires n'auraient aucune signification).

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