Java中double类型精度丢失的问题
PHPz
PHPz 2017-04-18 09:41:04
0
1
472

为什么使用add2方法的结果是错误的?Double.toString(d1)为什么能保持double的精度不丢失?

    public static double add(double d1, double d2) {
        BigDecimal bd1 = new BigDecimal(Double.toString(d1));
        BigDecimal bd2 = new BigDecimal(Double.toString(d2));
        return bd1.add(bd2).doubleValue();
    }
    
    public static double add2(double d1, double d2) {
        BigDecimal bd1 = new BigDecimal(d1);
        BigDecimal bd2 = new BigDecimal(d2);
        return bd1.add(bd2).doubleValue();
    }

    public static void main(String[] args) {
        double d1 = 1.0000002;
        double d2 = 0.0000002;
        System.out.println(Double.toString(d1));
        System.out.println("d1 + d2 = " + (d1 + d2)); // 1.0000003999999998
        System.out.println("d1 + d2 = " + add(d1, d2)); // 1.0000004
        System.out.println("d1 + d2 = " + add2(d1, d2)); // 1.0000003999999998
    }

PS:引出一个概念问题:

double d = XXXX.XXXXXX;
System.out.println(d);    //这里总是不会丢失精度的,为什么?
PHPz
PHPz

学习是最好的投资!

répondre à tous(1)
左手右手慢动作

Lorsque BigDecimal utilise double comme paramètre d'entrée, le binaire ne peut pas représenter avec précision les décimales. Une fois que le compilateur a lu les chaînes "0.0000002" et "1.0000002", il doit les convertir en une valeur double de 8 octets. C'est 1.0000001999999998947288304407265968620777130126953125 quelque chose comme ça. .
Ainsi, lors de l'exécution, la valeur réelle réellement transmise au constructeur BigDecimal est 1.0000001999999998947288304407265968620777130126953125.
BigDecimal peut convertir correctement la chaîne en un nombre à virgule flottante vraiment précis lors de l'utilisation de String comme paramètre d'entrée.

Dans la partie System.out.println, si le paramètre d'entrée est une chaîne, il sera directement sorti. Si le paramètre d'entrée est d'un autre type, la méthode Object.toString sera appelée pour la conversion puis sortie. Double.toString utilisera une certaine précision pour arrondir le double avant de le sortir.

Le commentaire sur le constructeur BigDecimal résout ce problème :

 The results of this constructor can be somewhat unpredictable.
     * One might assume that writing {@code new BigDecimal(0.1)} in
     * Java creates a {@code BigDecimal} which is exactly equal to
     * 0.1 (an unscaled value of 1, with a scale of 1), but it is
     * actually equal to
     * 0.1000000000000000055511151231257827021181583404541015625.
     * This is because 0.1 cannot be represented exactly as a
     * {@code double} (or, for that matter, as a binary fraction of
     * any finite length).  Thus, the value that is being passed
     * <i>in</i> to the constructor is not exactly equal to 0.1,
     * appearances notwithstanding.
     
     The {@code String} constructor, on the other hand, is
 * perfectly predictable: writing {@code new BigDecimal("0.1")}
 * creates a {@code BigDecimal} which is <i>exactly</i> equal to
 * 0.1, as one would expect.  Therefore, it is generally
 * recommended that the {@linkplain #BigDecimal(String)
 * <tt>String</tt> constructor} be used in preference to this one.
 
 

Explication supplémentaire :
Double.toStringCette méthode génère un String et sera arrondie. Le paramètre d'entrée
new BigDecimal(Double.toString(d1)) est un String après traitement, et le constructeur BigDecimal(String val) est appelé.
La méthode BigDecimal(String val) dans le code source traitera val dans un tableau char[] :

this(val.toCharArray(), 0, val.length());

Appelez ensuite le constructeur BigDecimal(char[] in).

Et les nouveaux appels BigDecimal(d1) BigDecimal(double val) La première chose après l'arrivée de cette méthode est

.
long valBits = Double.doubleToLongBits(val); 

Convertissez les paramètres d'entrée en binaire, la précision sera donc perdue.

La perte de précision causée par la double addition est la même que la situation ci-dessus. Elle est d'abord convertie en bits puis calculée, puis la précision est perdue. .

Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!