Pertanyaan Konversi float menjadi dua kali lipat tanpa kehilangan presisi


Saya memiliki pelampung primitif dan saya perlu sebagai primitif ganda. Cukup melayangkan float untuk menggandakan memberi saya ketelitian ekstra yang aneh. Sebagai contoh:

float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375

Namun, jika bukan casting, saya output float sebagai string, dan parse string sebagai double, saya mendapatkan apa yang saya inginkan:

System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35

Apakah ada cara yang lebih baik selain pergi ke String dan kembali?


76
2018-05-27 14:39


asal


Jawaban:


Bukan itu kamu sebenarnya mendapatkan ketepatan ekstra - itu bahwa pelampung tidak secara akurat mewakili nomor yang Anda tujukan untuk awalnya. Ganda aku s mewakili float asli secara akurat; toString menunjukkan data "ekstra" yang sudah ada.

Misalnya (dan angka-angka ini tidak benar, saya hanya mengada-ada) dukungan yang Anda miliki:

float f = 0.1F;
double d = f;

Maka nilai dari f mungkin tepat 0.100000234523. d akan memiliki nilai yang sama persis, tetapi ketika Anda mengonversinya menjadi string, ia akan "percaya" bahwa itu akurat untuk presisi yang lebih tinggi, jadi tidak akan membulatkan awal, dan Anda akan melihat "ekstra digit" yang sudah di sana, tetapi tersembunyi dari Anda.

Ketika Anda mengkonversi ke string dan kembali, Anda berakhir dengan nilai ganda yang lebih dekat dengan nilai string daripada float asli - tapi itu hanya bagus jika Anda benar-benar percaya bahwa nilai string adalah apa yang benar-benar Anda inginkan.

Apakah Anda yakin bahwa float / double adalah jenis yang tepat untuk digunakan di sini, bukan BigDecimal? Jika Anda mencoba menggunakan angka yang memiliki nilai desimal yang tepat (misalnya uang), lalu BigDecimal adalah tipe IMO yang lebih tepat.


102
2018-05-27 14:42



Saya menemukan konversi ke representasi biner lebih mudah untuk memahami masalah ini.

float f = 0.27f;
double d2 = (double) f;
double d3 = 0.27d;

System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d2)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d3)));

Anda dapat melihat float diperluas ke ganda dengan menambahkan 0s sampai akhir, tetapi bahwa representasi ganda dari 0,27 adalah 'lebih akurat', maka masalahnya.

   111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000

31
2018-02-07 08:45



Ini karena kontrak Float.toString(float), yang mengatakan sebagian:

Berapa banyak digit yang harus dicetak   bagian pecahan [...]? Sana   harus setidaknya satu digit   mewakili bagian pecahan, dan   melampaui itu sebanyak mungkin, tetapi hanya banyak, lebih banyak digit yang diperlukan untuk secara unik   membedakan nilai argumen dari   nilai yang berdekatan dari tipe float. Bahwa   adalah, anggaplah bahwa x adalah persis   nilai matematika yang diwakili oleh   representasi desimal yang dihasilkan oleh   metode ini untuk nol terbatas   argumen f. Maka f harus menjadi pelampung   nilai terdekat ke x; atau, jika dua mengambang   nilainya sama dengan x, maka f   pasti salah satunya dan paling tidak   sedikit signifikan dari significand dari   f harus 0.


22
2018-05-27 14:56



Saya telah mengalami masalah ini hari ini dan tidak dapat menggunakan refactor ke BigDecimal, karena proyek ini sangat besar. Namun saya menemukan solusi menggunakan

Float result = new Float(5623.23)
Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()

Dan ini berhasil.

Perhatikan bahwa memanggil hasil.doubleValue () mengembalikan 5623.22998046875

Tetapi memanggil doubleResult.doubleValue () mengembalikan dengan benar 5623.23

Tetapi saya tidak sepenuhnya yakin apakah itu solusi yang benar.


14
2018-01-02 12:24



Gunakan BigDecimaldari pada float/double. Ada banyak angka yang tidak dapat direpresentasikan sebagai floating point biner (misalnya, 0.1). Jadi Anda harus selalu membulatkan hasil ke ketepatan atau penggunaan yang diketahui BigDecimal.

Lihat http://en.wikipedia.org/wiki/Floating_point untuk informasi lebih lanjut.


6
2018-05-27 14:45



Saya menemukan solusi berikut:

public static Double getFloatAsDouble(Float fValue) {
    return Double.valueOf(fValue.toString());
}

Jika Anda menggunakan mengapung dan dua kali lipatdari pada Mengapung dan Dua kali lipat gunakan yang berikut ini:

public static double getFloatAsDouble(float value) {
    return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}

4
2018-01-25 16:20



Mengapung, secara alami, tidak tepat dan selalu memiliki "masalah" pembulatan yang rapi. Jika presisi itu penting maka Anda dapat mempertimbangkan refactoring aplikasi Anda untuk menggunakan Desimal atau BigDecimal.

Ya, mengapung secara komputasi lebih cepat dari desimal karena pada dukungan prosesor. Namun, apakah Anda ingin cepat atau akurat?


1
2018-05-27 14:55



Untuk informasi ini datang di bawah Item 48 - Hindari float dan dobel ketika nilai-nilai yang tepat diperlukan, dari Edisi 2 Java Efektif oleh Joshua Bloch. Buku ini dikemas dengan barang-barang bagus dan sangat menarik untuk dilihat.


0
2018-05-27 15:05



Apakah ini berfungsi?

float flt = 145.664454;

Double dbl = 0.0;
dbl += flt;

0
2018-03-04 12:18