Ketika membandingkan pelampung dengan bilangan bulat, beberapa pasang nilai membutuhkan waktu lebih lama untuk dievaluasi daripada nilai lain dengan besaran yang sama.
Sebagai contoh:
>>> import timeit
>>> timeit.timeit("562949953420000.7 < 562949953421000") # run 1 million times
0.5387085462592742
Tetapi jika float atau integer dibuat lebih kecil atau lebih besar dengan jumlah tertentu, perbandingan berjalan jauh lebih cepat:
>>> timeit.timeit("562949953420000.7 < 562949953422000") # integer increased by 1000
0.1481498428446173
>>> timeit.timeit("562949953423001.8 < 562949953421000") # float increased by 3001.1
0.1459577925548956
Mengubah operator perbandingan (misalnya menggunakan ==
atau >
sebagai gantinya) tidak mempengaruhi waktu dengan cara apa pun yang terlihat.
Ini tidak semata - mata terkait dengan besarnya karena memilih nilai yang lebih besar atau lebih kecil dapat menghasilkan perbandingan yang lebih cepat, jadi saya curiga ini tergantung pada beberapa cara yang disayangkan bit berbaris.
Jelas, membandingkan nilai-nilai ini lebih dari cukup cepat untuk sebagian besar kasus penggunaan. Saya hanya ingin tahu mengapa Python tampaknya lebih banyak berjuang dengan beberapa pasang nilai daripada dengan yang lain.
sumber
Jawaban:
Sebuah komentar dalam kode sumber Python untuk objek float mengakui bahwa:
Ini terutama benar ketika membandingkan float dengan integer, karena, tidak seperti float, integer dengan Python bisa berukuran besar dan selalu tepat. Mencoba melemparkan bilangan bulat ke pelampung mungkin kehilangan presisi dan membuat perbandingan tidak akurat. Mencoba melemparkan pelampung ke bilangan bulat tidak akan berhasil karena bagian pecahan akan hilang.
Untuk mengatasi masalah ini, Python melakukan serangkaian pemeriksaan, mengembalikan hasilnya jika salah satu pemeriksaan berhasil. Ini membandingkan tanda-tanda dari dua nilai, lalu apakah bilangan bulat "terlalu besar" untuk menjadi pelampung, kemudian membandingkan eksponen float dengan panjang bilangan bulat. Jika semua pemeriksaan ini gagal, perlu untuk membangun dua objek Python baru untuk membandingkan untuk mendapatkan hasilnya.
Ketika membandingkan float
v
dengan integer / longw
, kasus terburuknya adalah:v
danw
memiliki tanda yang sama (baik positif atau negatif),w
memiliki beberapa bit yang cukup yang dapat disimpan dalamsize_t
tipe (biasanya 32 atau 64 bit),w
memiliki setidaknya 49 bit,v
sama dengan jumlah bit dalamw
.Dan inilah tepatnya yang kita miliki untuk nilai-nilai dalam pertanyaan:
Kita melihat bahwa 49 adalah eksponen float dan jumlah bit dalam bilangan bulat. Kedua angka positif dan keempat kriteria di atas terpenuhi.
Memilih salah satu nilai yang lebih besar (atau lebih kecil) dapat mengubah jumlah bit integer, atau nilai eksponen, sehingga Python dapat menentukan hasil perbandingan tanpa melakukan pemeriksaan akhir yang mahal.
Ini khusus untuk implementasi bahasa CPython.
Perbandingannya lebih detail
The
float_richcompare
Fungsi menangani perbandingan antara dua nilaiv
danw
.Di bawah ini adalah deskripsi langkah-demi-langkah dari pemeriksaan yang dilakukan fungsi. Komentar dalam sumber Python sebenarnya sangat membantu ketika mencoba memahami apa fungsinya, jadi saya meninggalkannya di tempat yang relevan. Saya juga meringkas cek-cek ini dalam daftar di bagian bawah jawabannya.
Ide utamanya adalah untuk memetakan objek Python
v
danw
dua C yang sesuai ganda,i
danj
, yang kemudian dapat dengan mudah dibandingkan untuk memberikan hasil yang benar. Baik Python 2 dan Python 3 menggunakan ide yang sama untuk melakukan ini (yang sebelumnya hanya menanganiint
danlong
mengetik secara terpisah).Hal pertama yang harus dilakukan adalah memeriksa apakah
v
itu benar-benar float Python dan memetakannya ke C gandai
. Selanjutnya fungsi melihat apakahw
juga float dan memetakannya ke C gandaj
. Ini adalah skenario terbaik untuk fungsi karena semua pemeriksaan lainnya dapat dilewati. Pemeriksaan fungsi juga untuk melihat apakahv
adalahinf
ataunan
:Sekarang kita tahu bahwa jika
w
gagal memeriksa ini, itu bukan float Python. Sekarang fungsinya memeriksa apakah itu bilangan bulat Python. Jika demikian, tes termudah adalah mengekstraksi tandav
dan tandaw
(kembali0
jika nol,-1
jika negatif,1
jika positif). Jika tanda-tandanya berbeda, ini semua informasi yang diperlukan untuk mengembalikan hasil perbandingan:Jika pemeriksaan ini gagal, maka
v
danw
memiliki tanda yang sama.Pemeriksaan selanjutnya menghitung jumlah bit dalam bilangan bulat
w
. Jika bitnya terlalu banyak, maka tidak mungkin disimpan sebagai float dan karenanya harus lebih besar daripada floatv
:Di sisi lain, jika integer
w
memiliki 48 bit atau lebih sedikit, ia dapat dengan aman diubah menjadi C gandaj
dan membandingkan:Dari titik ini dan seterusnya, kita tahu bahwa
w
memiliki 49 bit atau lebih. Akan nyaman untuk diperlakukanw
sebagai bilangan bulat positif, jadi ubah tanda dan operator pembanding sesuai kebutuhan:Sekarang fungsinya terlihat pada eksponen float. Ingat bahwa pelampung dapat ditulis (tanda abaikan) sebagai signifikansi * 2 eksponen dan yang signifikan mewakili angka antara 0,5 dan 1:
Ini memeriksa dua hal. Jika eksponen kurang dari 0 maka float lebih kecil dari 1 (dan besarnya lebih kecil dari bilangan bulat apa pun). Atau, jika eksponen kurang dari jumlah bit
w
maka kita memiliki ituv < |w|
karena signifikansi * 2 eksponen kurang dari 2 nbits .Gagal dari kedua pemeriksaan ini, fungsinya terlihat untuk melihat apakah eksponen lebih besar dari jumlah bit
w
. Ini menunjukkan bahwa signifikansi * 2 eksponen lebih besar dari 2 nbits dan karenanyav > |w|
:Jika pemeriksaan ini tidak berhasil, kita tahu bahwa eksponen float
v
sama dengan jumlah bit dalam bilangan bulatw
.Satu-satunya cara kedua nilai dapat dibandingkan sekarang adalah dengan membangun dua bilangan bulat Python baru dari
v
danw
. Idenya adalah untuk membuang bagian fraksionalv
, menggandakan bagian integer, dan kemudian menambahkan satu.w
juga dua kali lipat dan dua objek Python baru ini dapat dibandingkan untuk memberikan nilai kembali yang benar. Menggunakan contoh dengan nilai kecil,4.65 < 4
akan ditentukan oleh perbandingan(2*4)+1 == 9 < 8 == (2*4)
(return false).Untuk singkatnya saya telah meninggalkan tambahan pemeriksaan kesalahan dan pelacakan sampah yang harus dilakukan Python ketika membuat objek baru ini. Tidak perlu dikatakan, ini menambah overhead tambahan dan menjelaskan mengapa nilai-nilai yang disorot dalam pertanyaan jauh lebih lambat dibandingkan dibandingkan yang lain.
Berikut ini adalah ringkasan dari pemeriksaan yang dilakukan oleh fungsi perbandingan.
Membiarkan
v
menjadi pelampung dan melemparkannya sebagai C ganda. Sekarang, jikaw
juga pelampung:Periksa apakah
w
ininan
atauinf
. Jika demikian, pegang kasing khusus ini secara terpisah tergantung pada jenisw
.Jika tidak, bandingkan
v
danw
langsung dengan representasi mereka sebagai C berlipat ganda.Jika
w
bilangan bulat:Ekstrak tanda-tanda
v
danw
. Jika mereka berbeda maka kita tahuv
danw
berbeda dan mana yang nilainya lebih besar.( Tanda-tandanya sama. ) Periksa apakah
w
bit terlalu banyak untuk mengapung (lebih darisize_t
). Jika demikian,w
memiliki besaran lebih besar dariv
.Periksa apakah
w
memiliki 48 bit atau lebih sedikit. Jika demikian, dapat dengan aman dilemparkan ke C ganda tanpa kehilangan presisi dan dibandingkan denganv
.(
w
memiliki lebih dari 48 bit. Sekarang kami akan memperlakukanw
sebagai bilangan bulat positif setelah mengubah op pembanding yang sesuai. )Pertimbangkan eksponen float
v
. Jika eksponen negatif, makav
kurang dari1
dan karena itu kurang dari bilangan bulat positif. Lain, jika eksponen kurang dari jumlah bitw
maka itu harus kurang dariw
.Jika eksponen
v
lebih besar dari jumlah bitw
makav
lebih besar dariw
.( Eksponen sama dengan jumlah bit dalam
w
. )Pemeriksaan terakhir. Dibagi
v
menjadi bagian bilangan bulat dan pecahannya. Gandakan bagian integer dan tambahkan 1 untuk mengkompensasi bagian fraksional. Sekarang gandakan integerw
. Bandingkan kedua bilangan bulat baru ini untuk mendapatkan hasilnya.sumber
Menggunakan
gmpy2
dengan float presisi presisi dan bilangan bulat dimungkinkan untuk mendapatkan kinerja perbandingan yang lebih seragam:sumber
decimal
di perpustakaan standar