Mengapa beberapa angka kehilangan akurasi ketika disimpan sebagai angka floating point?
Sebagai contoh, angka desimal 9.2
dapat dinyatakan dengan tepat sebagai rasio dua bilangan bulat desimal ( 92/10
), yang keduanya dapat dinyatakan secara tepat dalam biner ( 0b1011100/0b1010
). Namun, rasio yang sama disimpan sebagai angka floating point tidak pernah sama persis dengan 9.2
:
32-bit "single precision" float: 9.19999980926513671875
64-bit "double precision" float: 9.199999999999999289457264239899814128875732421875
Bagaimana angka yang tampaknya sederhana ini "terlalu besar" untuk diekspresikan dalam memori 64 bit ?
floating-point
language-agnostic
precision
mhlester
sumber
sumber
Jawaban:
Dalam sebagian besar bahasa pemrograman, angka floating point diwakili banyak seperti notasi ilmiah : dengan eksponen dan mantra (juga disebut signifikansi). Angka yang sangat sederhana, katakanlah
9.2
, sebenarnya adalah fraksi ini:Di mana eksponen
-49
dan mantissa berada5179139571476070
. Alasan mengapa tidak mungkin untuk mewakili beberapa angka desimal dengan cara ini adalah bahwa eksponen dan mantissa harus bilangan bulat. Dengan kata lain, semua pelampung harus berupa bilangan bulat dikalikan dengan kekuatan bilangan bulat 2 .9.2
mungkin sederhana92/10
, tetapi 10 tidak dapat dinyatakan sebagai 2 n jika n terbatas pada nilai integer.Melihat Data
Pertama, beberapa fungsi untuk melihat komponen yang menghasilkan 32- dan 64-bit
float
. Hapus ini jika Anda hanya peduli dengan output (contoh dalam Python):Ada banyak kerumitan di balik fungsi itu, dan itu akan cukup jelas untuk dijelaskan, tetapi jika Anda tertarik, sumber daya penting untuk tujuan kita adalah modul struct .
Python
float
adalah angka 64-bit, presisi ganda. Dalam bahasa lain seperti C, C ++, Java dan C #, presisi ganda memiliki tipe terpisahdouble
, yang sering diimplementasikan sebagai 64 bit.Ketika kita memanggil fungsi itu dengan contoh kita
9.2
, inilah yang kita dapatkan:Menafsirkan Data
Anda akan melihat saya telah membagi nilai kembali menjadi tiga komponen. Komponen-komponen ini adalah:
Tanda
Tanda disimpan dalam komponen pertama sebagai bit tunggal. Sangat mudah dijelaskan:
0
berarti pelampung adalah angka positif;1
berarti negatif. Karena9.2
positif, nilai tanda kami adalah0
.Eksponen
Eksponen disimpan di komponen tengah sebagai 11 bit. Dalam kasus kami
0b10000000010
,. Dalam desimal, itu mewakili nilai1026
. Keunikan dari komponen ini adalah Anda harus mengurangi angka sama dengan 2 (# bit) - 1 - 1 untuk mendapatkan eksponen yang benar; dalam kasus kami, itu berarti mengurangi0b1111111111
(angka desimal1023
) untuk mendapatkan eksponen yang sebenarnya,0b00000000011
(angka desimal 3).Mantissa
Mantera disimpan dalam komponen ketiga sebagai 52 bit. Namun, ada kekhasan untuk komponen ini juga. Untuk memahami kekhasan ini, pertimbangkan nomor dalam notasi ilmiah, seperti ini:
Mantera akan menjadi
6.0221413
. Ingatlah bahwa mantissa dalam notasi ilmiah selalu dimulai dengan satu digit bukan nol. Hal yang sama berlaku untuk biner, kecuali bahwa biner hanya memiliki dua digit:0
dan1
. Jadi mantissa biner selalu dimulai dengan1
! Ketika pelampung disimpan,1
bagian depan biner mantissa dihilangkan untuk menghemat ruang; kita harus meletakkannya kembali di depan elemen ketiga kita untuk mendapatkan mantissa yang sebenarnya :Ini melibatkan lebih dari sekedar tambahan sederhana, karena bit yang disimpan dalam komponen ketiga kami sebenarnya mewakili bagian fraksional mantissa, di sebelah kanan titik radix .
Ketika berhadapan dengan angka desimal, kita "memindahkan titik desimal" dengan mengalikan atau membagi dengan kekuatan 10. Dalam biner, kita dapat melakukan hal yang sama dengan mengalikan atau membagi dengan kekuatan 2. Karena elemen ketiga kita memiliki 52 bit, kita membagi dengan 2 52 untuk memindahkannya 52 tempat ke kanan:
Dalam notasi desimal, itu sama dengan membagi
675539944105574
oleh4503599627370496
untuk mendapatkan0.1499999999999999
. (Ini adalah salah satu contoh rasio yang dapat dinyatakan secara persis dalam biner, tetapi hanya kurang dalam desimal; untuk lebih jelasnya , lihat: 675539944105574/4503599627370496 .)Sekarang kita telah mengubah komponen ketiga menjadi angka fraksional, menambahkan
1
memberi mantissa yang sebenarnya.Memasang Kembali Komponen
0
untuk positif,1
untuk negatif1
untuk mendapatkan mantissa yang sebenarnyaMenghitung Angka
Menyatukan ketiga bagian ini, kami diberi nomor biner ini:
Yang kemudian dapat kita konversi dari biner ke desimal:
Dan kalikan untuk mengungkapkan representasi akhir dari jumlah kami mulai dengan (
9.2
) setelah disimpan sebagai nilai floating point:Mewakili sebagai Fraksi
9.2
Sekarang kita telah membangun nomornya, adalah mungkin untuk merekonstruksi menjadi pecahan sederhana:
Pindahkan mantissa ke seluruh nomor:
Konversikan ke desimal:
Kurangi eksponen:
Ubah eksponen negatif menjadi divisi:
Gandakan eksponen:
Yang sama dengan:
9.5
Sudah Anda dapat melihat mantissa hanya 4 digit diikuti oleh banyak nol. Tapi mari kita pergi melalui langkah.
Merakit notasi ilmiah biner:
Menggeser titik desimal:
Kurangi eksponen:
Biner ke desimal:
Eksponen negatif ke divisi:
Gandakan eksponen:
Sama dengan:
Bacaan lebih lanjut
sumber
Ini bukan jawaban lengkap ( mhlester sudah membahas banyak hal baik yang tidak akan saya tiru), tetapi saya ingin menekankan seberapa besar representasi angka tergantung pada basis tempat Anda bekerja.
Pertimbangkan fraksi 2/3
Dalam basis 10 yang bagus, kami biasanya menuliskannya sebagai sesuatu seperti
Ketika kita melihat representasi itu, kita cenderung mengasosiasikan masing-masing dengan fraksi 2/3, meskipun hanya representasi pertama secara matematis sama dengan fraksi. Representasi / perkiraan kedua dan ketiga memiliki kesalahan pada urutan 0,001, yang sebenarnya jauh lebih buruk daripada kesalahan antara 9,2 dan 9,1999999999999993. Bahkan, representasi kedua bahkan tidak dibulatkan dengan benar! Namun demikian, kami tidak memiliki masalah dengan 0,666 sebagai perkiraan dari angka 2/3, jadi kami seharusnya tidak benar-benar memiliki masalah dengan bagaimana 9,2 diperkirakan di sebagian besar program . (Ya, dalam beberapa program itu penting.)
Basis nomor
Jadi di sinilah basis bilangan sangat penting. Jika kami mencoba mewakili 2/3 di basis 3, maka
Dengan kata lain, kami memiliki representasi yang tepat dan terbatas untuk nomor yang sama dengan mengganti basis! Kesimpulannya adalah bahwa meskipun Anda dapat mengonversi angka apa pun ke pangkalan apa pun, semua bilangan rasional memiliki representasi terbatas yang tepat di beberapa pangkalan tetapi tidak di yang lain .
Untuk mengantar titik ini pulang, mari kita lihat 1/2. Mungkin mengejutkan Anda bahwa meskipun angka yang sangat sederhana ini memiliki representasi yang tepat di basis 10 dan 2, ia membutuhkan representasi berulang di basis 3.
Mengapa angka floating point tidak akurat?
Karena sering kali, mereka mendekati rasional yang tidak dapat diwakili secara terbatas dalam basis 2 (angka-angka berulang), dan secara umum mereka mendekati angka nyata (mungkin tidak rasional) yang mungkin tidak dapat diwakili dalam banyak digit dalam basis apa pun .
sumber
1/3
seperti base-10 sempurna untuk1/10
. Fraksi tidak bekerja di basis-2N
atau kelipatannya.π
dll dibatalkan.Meskipun semua jawaban lain baik, masih ada satu hal yang hilang:
Tidak mungkin untuk mewakili bilangan irasional (misalnya π
sqrt(2)
,,log(3)
, dll) tepatnya!Dan itulah mengapa mereka disebut irasional. Tidak ada jumlah penyimpanan bit di dunia akan cukup untuk menampung bahkan satu dari mereka. Hanya simbolis aritmatika yang mampu mempertahankan presisi mereka.
Meskipun jika Anda akan membatasi kebutuhan matematika Anda ke bilangan rasional hanya masalah ketelitian menjadi dapat dikelola. Anda perlu menyimpan sepasang bilangan bulat (mungkin sangat besar)
a
danb
menahan angka yang diwakili oleh fraksia/b
. Semua aritmatika Anda harus dilakukan pada pecahan seperti dalam matematika sekolah menengah (misalnyaa/b * c/d = ac/bd
).Tapi tentu saja Anda masih akan mengalami yang sama kesulitan ketika
pi
,sqrt
,log
,sin
, dll yang terlibat.TL; DR
Untuk aritmatika yang dipercepat perangkat keras, hanya sejumlah angka rasional yang dapat direpresentasikan. Setiap angka yang tidak dapat diwakili diperkirakan. Beberapa angka (yaitu irasional) tidak pernah dapat direpresentasikan tidak peduli sistemnya.
sumber
Ada bilangan real yang tak terhingga banyaknya (begitu banyak sehingga Anda tidak dapat menghitungnya), dan ada banyak bilangan rasional tak terhingga (dimungkinkan untuk menghitungnya).
Representasi floating-point adalah yang terbatas (seperti apa pun di komputer) sehingga banyak sekali banyak angka yang tidak mungkin untuk diwakili. Secara khusus, 64 bit hanya memungkinkan Anda untuk membedakan hanya 18.446.744.073.709.551.616 nilai yang berbeda (yang tidak seberapa dibandingkan dengan tak terbatas). Dengan konvensi standar, 9.2 bukan salah satunya. Yang bisa dari bentuk m.2 ^ e untuk beberapa bilangan bulat m dan e.
Anda mungkin datang dengan sistem penomoran yang berbeda, 10 berbasis misalnya, di mana 9,2 akan memiliki representasi yang tepat. Tetapi angka lainnya, katakanlah 1/3, masih tidak mungkin untuk diwakili.
Juga perhatikan bahwa angka floating-point presisi ganda sangat akurat. Mereka dapat mewakili angka apa pun dalam rentang yang sangat luas dengan sebanyak 15 digit tepat. Untuk perhitungan kehidupan sehari-hari, 4 atau 5 digit lebih dari cukup. Anda tidak akan pernah benar-benar membutuhkan 15 itu, kecuali jika Anda ingin menghitung setiap milidetik dalam hidup Anda.
sumber
Nomor titik mengambang adalah (menyederhanakan sedikit) sistem penomoran posisi dengan jumlah digit terbatas dan titik radix bergerak.
Fraksi hanya dapat diekspresikan dengan tepat menggunakan jumlah digit terbatas dalam sistem penomoran posisi jika faktor utama penyebut (ketika fraksi dinyatakan dalam istilah terendah) adalah faktor basis.
Faktor prima dari 10 adalah 5 dan 2, jadi pada basis 10 kita dapat mewakili sebagian kecil dari bentuk a / (2 b 5 c ).
Di sisi lain satu-satunya faktor prima dari 2 adalah 2, jadi pada basis 2 kita hanya dapat mewakili fraksi dari bentuk a / (2 b )
Karena ini adalah format sederhana untuk dikerjakan dan cukup akurat untuk sebagian besar tujuan. Pada dasarnya alasan yang sama para ilmuwan menggunakan "notasi ilmiah" dan membulatkan hasil mereka ke jumlah digit yang wajar pada setiap langkah.
Tentunya dimungkinkan untuk menentukan format fraksi, dengan (misalnya) pembilang 32-bit dan penyebut 32-bit. Itu akan dapat mewakili angka-angka yang tidak dapat diapung oleh floating point presisi IEEE, tetapi juga akan ada banyak angka yang dapat diwakili dalam floating point presisi ganda yang tidak dapat diwakili dalam format fraksi ukuran tetap.
Namun masalah besar adalah format seperti itu sulit dilakukan perhitungan. Karena dua alasan.
Beberapa Bahasa memang menawarkan jenis fraksi, tetapi biasanya mereka melakukannya dalam kombinasi dengan presisi arbiter, ini menghindari perlu khawatir tentang mendekati fraksi tetapi itu menciptakan masalah sendiri, ketika angka melewati sejumlah besar perhitungan langkah ukuran penyebut dan maka penyimpanan yang dibutuhkan untuk fraksi dapat meledak.
Beberapa bahasa juga menawarkan tipe floating point desimal, ini terutama digunakan dalam skenario di mana penting bahwa hasil komputer sesuai dengan aturan pembulatan yang sudah ada sebelumnya yang ditulis dengan manusia dalam pikiran (terutama perhitungan keuangan). Ini sedikit lebih sulit untuk dikerjakan daripada binary floating point, tetapi masalah terbesarnya adalah kebanyakan komputer tidak menawarkan dukungan perangkat keras untuk mereka.
sumber
Coba ini
'
decimalValue
' adalah nilai Anda untuk dikonversi.sumber