Pertanyaan Apakah inkonsistensi dalam pembulatan antara Java 7 dan Java 8 bug?


Saya melihat inkonsistensi dalam pembulatan di DecimalFormat kelas antara java 7 dan java 8. Berikut adalah test case saya:

DesimalFormatTest.java

import java.text.DecimalFormat;

public class DecimalFormatTest {
    public static void main(String[] args) {
        DecimalFormat format = (DecimalFormat) DecimalFormat.getInstance();
        format.setDecimalSeparatorAlwaysShown(true);
        format.setMinimumFractionDigits(1);
        format.setMaximumFractionDigits(1);
        System.out.println(format.format(83.65));
    }
}

Di Java (TM) SE Runtime Environment (build 1.7.0_51-b13) outputnya adalah:

83.6

Di Java (TM) SE Runtime Environment (build 1.8.0-b132), outputnya adalah:

83.7

Apakah ini bug regresi? Ataukah peraturan pembulatan berubah dengan dirilisnya Java 8?


32
2018-04-01 22:14


asal


Jawaban:


Sepertinya ini adalah bug lama di JDK 7 yang akhirnya diperbaiki. Lihat misalnya:

Ada rencana draf untuk memberikan saran berikut dengan JDK 8 yang menjelaskan masalah ini:

-------------------------------------------------- ------------------- Area: Inti Perpustakaan / java.text

Ringkasan: Perilaku pembulatan yang salah dari JDK7 telah diperbaiki. Itu   pembulatan perilaku format NumberFormat / DecimalFormat () metode memiliki   berubah ketika nilai sangat dekat dengan dasi duduk tepat di   posisi pembulatan yang ditentukan dalam pola pemformatan.

Sifat Ketidakcocokan: perilaku

Deskripsi: Saat menggunakan kelas NumberFormat / DesimalFormat,   pembulatan perilaku versi JDK sebelumnya salah di beberapa sudut   kasus. Perilaku yang salah ini terjadi ketika memanggil format () metode dengan   nilai yang sangat dekat dengan dasi, sementara posisi pembulatan ditentukan   oleh pola contoh NumberFormat / DesimalFormat yang digunakan adalah   tepatnya duduk di posisi dasi. Dalam hal itu salah ganda   pembulatan atau perilaku pembulatan yang salah terjadi.

Sebagai contoh, ketika menggunakan default yang direkomendasikan NumberFormatFormat API   bentuk: NumberFormat nf = java.text.NumberFormat.getInstance() diikuti   oleh nf.format(0.8055d), nilai 0.8055d dicatat di komputer sebagai    0.80549999999999999378275106209912337362766265869140625 karena nilai ini tidak dapat diwakili dengan tepat dalam format biner. Di sini default   Aturan pembulatan adalah "setengah-genap", dan hasil pemanggilan format () dalam   JDK7 adalah output salah "0,806", sementara hasil yang benar adalah "0,805"   karena nilai yang terekam dalam memori oleh komputer adalah "di bawah" dasi.

Perilaku baru ini juga diterapkan untuk semua posisi pembulatan   yang dapat didefinisikan oleh pola apa pun yang dipilih oleh programmer (non   yang default).

RFE          7131459



20
2018-04-01 22:22



Seperti disebutkan dalam jawaban lain untuk pertanyaan ini, JDK 8 dibuat disengaja berubah menjadi DecimalFormat membulatkan masalah JDK-7131459: DecimalFormat menghasilkan format salah () hasil saat mendekati dasi.

Namun, perubahan itu memperkenalkan bug asli diajukan sebagai JDK-8039915: Wrong NumberFormat.format () HALF_UP pembulatan ketika digit terakhir tepat pada posisi pembulatan lebih besar dari 5. Sebagai contoh:

99.9989 -> 100.00
99.9990 ->  99.99

Sederhananya, ini menunjukkan kasus di mana jumlah putaran yang lebih tinggi turun, dan jumlah putaran yang lebih rendah naik: (x <= y) != (round(x) <= round(y)). Tampaknya hanya mempengaruhi HALF_UP pembulatan mode, yang merupakan jenis pembulatan diajarkan di kelas aritmatika kelas sekolah: 0,5 putaran jauh dari nol, selalu.

Masalah ini ada di kedua rilis Oracle dan OpenJDK dari Java 8 dan pembaruan 8u5, 8u11, 8u20, 8u25, dan 8u31.

Oracle memperbaiki bug ini di pembaruan Java 8 40

Patch runtime tidak resmi tersedia untuk versi sebelumnya

Terima kasih untuk penelitian oleh Holger di jawaban ini untuk pertanyaan terkait, saya bisa mengembangkan patch runtime dan perusahaan saya telah merilisnya secara gratis di bawah ketentuan lisensi GPLv2 dengan Exception Classpath1 (sama dengan kode sumber OpenJDK).

Proyek tambalan dan kode sumbernya dihosting di GitHub dengan detail lebih lanjut tentang bug ini serta tautan ke binari yang dapat diunduh. Tambalan ini bekerja saat runtime sehingga tidak ada modifikasi yang dilakukan pada file Java pada disk, dan seharusnya aman untuk digunakan pada semua versi Java Oracle> = 6 dan setidaknya melalui versi 8 (termasuk u40 dan yang lebih baru).

1 Saya bukan seorang pengacara, tetapi pemahaman saya adalah itu GPLv2 dengan CPE memungkinkan penggunaan komersial di bentuk biner tanpa GPL berlaku untuk pekerjaan gabungan.


14
2018-05-09 20:16