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

为什么使用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

学习是最好的投资!

membalas semua(1)
左手右手慢动作

Apabila BigDecimal menggunakan dua kali ganda sebagai parameter input, perduaan tidak boleh mewakili perpuluhan dengan tepat Selepas pengkompil membaca rentetan "0.0000002" dan "1.0000002", ia mesti menukarnya kepada nilai berganda 8 bait Ia adalah 1.0000001999999998947288304407265968620777130126953125 seperti ini .
Jadi, apabila dijalankan, nilai sebenar sebenarnya dihantar kepada pembina BigDecimal ialah 1.0000001999999998947288304407265968620777130126953125.
BigDecimal boleh menukar rentetan dengan betul kepada nombor titik terapung yang benar-benar tepat apabila menggunakan Rentetan sebagai parameter input.

Dalam bahagian System.out.println, jika parameter input ialah rentetan, ia akan dikeluarkan secara langsung Jika parameter input adalah jenis lain, kaedah Object.toString akan dipanggil untuk penukaran dan kemudian output. Double.toString akan menggunakan ketepatan tertentu untuk membundarkan double sebelum mengeluarkannya.

Komen pada pembina BigDecimal menangani isu ini:

 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.
 
 

Penjelasan tambahan:
Double.toStringKaedah ini menghasilkan String dan akan dibundarkan. Parameter input
new BigDecimal(Double.toString(d1)) ialah String selepas pemprosesan, dan pembina BigDecimal(String val) dipanggil.
Kaedah BigDecimal(String val) dalam kod sumber akan memproses val menjadi tatasusunan char[]:

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

Kemudian panggil BigDecimal(char[] in) pembina.

Dan panggilan BigDecimal(d1) baharu BigDecimal(double val) Perkara pertama selepas kaedah ini masuk ialah

long valBits = Double.doubleToLongBits(val); 

Tukar parameter input kepada binari, jadi ketepatan akan hilang.

Kehilangan ketepatan yang disebabkan oleh penambahan berganda adalah sama seperti keadaan di atas Ia mula-mula ditukar kepada bit dan kemudian dikira, dan kemudian ketepatan hilang. .

Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan