Pertanyaan Python modulo pada pelampung


Adakah yang bisa menjelaskan bagaimana operator modulo bekerja dengan Python? Saya tidak bisa mengerti mengapa 3.5 % 0.1 = 0.1.


25
2018-02-08 00:29


asal


Jawaban:


Sebenarnya, itu tidak benar 3.5 % 0.1 aku s 0.1. Anda dapat menguji ini dengan sangat mudah:

>>> print(3.5 % 0.1)
0.1
>>> print(3.5 % 0.1 == 0.1)
False

Sebenarnya, pada kebanyakan sistem, 3.5 % 0.1 aku s 0.099999999999999811. Tapi, pada beberapa versi Python, str(0.099999999999999811) aku s 0.1:

>>> 3.5 % 0.1
0.099999999999999811
>>> repr(3.5 % 0.1)
'0.099999999999999811'
>>> str(3.5 % 0.1)
'0.1'

Sekarang, Anda mungkin bertanya-tanya mengapa 3.5 % 0.1 aku s 0.099999999999999811 dari pada 0.0. Itu karena masalah pembulatan floating point biasa. Jika Anda belum membaca Apa yang Setiap Ilmuwan Komputer Harus Ketahui Tentang Aritmatika Titik-Mengambang, Anda harus — atau setidaknya singkatnya Wikipedia ringkasan masalah khusus ini.

Perhatikan juga itu 3.5/0.1 tidak 34, itu 35. Begitu, 3.5/0.1 * 0.1 + 3.5%0.1 aku s 3.5999999999999996, yang bahkan tidak dekat untuk 3.5. Ini cukup mendasar untuk definisi modulus, dan itu salah dalam Python, dan hampir setiap bahasa pemrograman lainnya.

Tetapi Python 3 datang untuk menyelamatkan di sana. Kebanyakan orang yang mengetahuinya // tahu bahwa ini adalah bagaimana Anda melakukan "pembagian integer" antara bilangan bulat, tetapi tidak menyadari bahwa ini adalah bagaimana Anda melakukan pembagian modulus-kompatibel apa saja jenis. 3.5//0.1 aku s 34.0, jadi 3.5//0.1 * 0.1 + 3.5%0.1 adalah (setidaknya dalam kesalahan pembulatan kecil) 3.5. Ini telah dilaporkan kembali ke 2.x, jadi (tergantung pada versi dan platform Anda) Anda mungkin dapat mengandalkan ini. Dan, jika tidak, Anda bisa menggunakannya divmod(3.5, 0.1), yang mengembalikan (dalam kesalahan pembulatan) (34.0, 0.09999999999999981) sepanjang jalan kembali ke kabut waktu. Tentu saja Anda masih mengharapkan ini terjadi (35.0, 0.0)tidak (34.0, almost-0.1), tetapi Anda tidak dapat memilikinya karena kesalahan pembulatan.

Jika Anda mencari perbaikan cepat, pertimbangkan untuk menggunakan Decimal mengetik:

>>> from decimal import Decimal
>>> Decimal('3.5') % Decimal('0.1')
Decimal('0.0')
>>> print(Decimal('3.5') % Decimal('0.1'))
0.0
>>> (Decimal(7)/2) % (Decimal(1)/10)
Decimal('0.0')

Ini bukan obat mujarab ajaib - misalnya, Anda masih harus berurusan dengan kesalahan pembulatan setiap kali nilai pasti dari sebuah operasi tidak cukup representatif dalam basis 10 - tetapi kesalahan pembulatan berbaris lebih baik dengan kasus-kasus intuisi manusia mengharapkan menjadi bermasalah. (Ada juga keuntungan untuk Decimal lebih float karena Anda dapat menentukan langkah-langkah eksplisit, melacak digit signifikan, dll, dan dalam hal itu sebenarnya sama di semua versi Python dari 2,4 hingga 3,3, sementara detail tentang float telah berubah dua kali dalam waktu bersamaan. Hanya saja itu tidak sempurna, karena itu tidak mungkin.) Tapi ketika Anda tahu sebelumnya bahwa nomor Anda semuanya dapat mewakili dalam basis 10, dan mereka tidak membutuhkan lebih banyak digit daripada ketepatan yang Anda konfigurasikan, itu akan bekerja. .


54
2018-02-08 00:44



Modulo memberi Anda rest dari sebuah divisi. 3.5 dibagi dengan 0.1 harus memberi Anda 35 dengan sisa 0. Tapi karena mengapung didasarkan pada kekuatan dua angka tidak tepat dan Anda mendapatkan kesalahan pembulatan.

Jika Anda perlu pembagian angka desimal Anda untuk tepatnya menggunakan modul desimal:

>>> from decimal import Decimal
>>> Decimal('3.5') / Decimal('0.1')
Decimal('35')
>>> Decimal('3.5') % Decimal('0.1')
Decimal('0.0')

Karena saya dihujat bahwa jawaban saya menyesatkan, inilah keseluruhan cerita:

0.1 sedikit lebih besar dari 0.1

>>> '%.50f' % 0.1
'0.10000000000000000555111512312578270211815834045410'

Jika Anda membagi float 3.5 dengan angka seperti itu, Anda mendapatkan istirahat hampir 0.1.

Mari kita mulai dengan nomornya 0.11 dan terus menambahkan nol di antara keduanya 1 digit untuk membuatnya lebih kecil sambil mempertahankannya lebih besar dari 0.1.

>>> '%.10f' % (3.5 % 0.101)
'0.0660000000'
>>> '%.10f' % (3.5 % 0.1001)
'0.0966000000'
>>> '%.10f' % (3.5 % 0.10001)
'0.0996600000'
>>> '%.10f' % (3.5 % 0.100001)
'0.0999660000'
>>> '%.10f' % (3.5 % 0.1000001)
'0.0999966000'
>>> '%.10f' % (3.5 % 0.10000001)
'0.0999996600'
>>> '%.10f' % (3.5 % 0.100000001)
'0.0999999660'
>>> '%.10f' % (3.5 % 0.1000000001)
'0.0999999966'
>>> '%.10f' % (3.5 % 0.10000000001)
'0.0999999997'
>>> '%.10f' % (3.5 % 0.100000000001)
'0.1000000000'

Baris terakhir memberi kesan bahwa kita akhirnya telah mencapai 0.1 tetapi mengubah string format mengungkapkan sifat asli:

>>> '%.20f' % (3.5 % 0.100000000001)
'0.09999999996600009156'

Format float default dari python hanya tidak menunjukkan presisi yang cukup sehingga 3.5 % 0.1 = 0.1 dan 3.5 % 0.1 = 35.0. Itu benar 3.5 % 0.100000... = 0.999999... dan 3.5 / 0.100000... = 34.999999..... Dalam hal pembagian Anda bahkan berakhir dengan tepat hasilkan sebagai 34.9999... secara ultimatif dibulatkan ke 35.0.


Fakta menyenangkan: Jika Anda menggunakan angka yang sedikit lebih kecil dari 0.1 dan melakukan operasi yang sama Anda berakhir dengan angka yang sedikit lebih besar dari 0:

>>> 1.0 - 0.9
0.09999999999999998
>>> 35.0 % (1.0 - 0.9)
7.771561172376096e-15
>>> '%.20f' % (35.0 % (1.0 - 0.9))
'0.00000000000000777156'

Menggunakan C ++ Anda bahkan dapat menunjukkan itu 3.5 dibagi dengan float 0.1 tidak 35 tetapi sesuatu yang sedikit lebih kecil.

#include <iostream>
#include <iomanip>

int main(int argc, char *argv[]) {
    // double/float, rounding errors do not cancel out
    std::cout << "double/float: " << std::setprecision(20) << 3.5 / 0.1f << std::endl;
    // double/double, rounding errors cancel out
    std::cout << "double/double: " << std::setprecision(20) << 3.5 / 0.1 << std::endl;
    return 0;
}

http://ideone.com/fTNVho

Dengan Python 3.5 / 0.1 memberi Anda hasil yang tepat dari 35 karena kesalahan pembulatan membatalkan satu sama lain. Itu benar 3.5 / 0.100000... = 34.9999999.... Dan 34.9999... terlalu panjang hingga akhirnya Anda benar-benar tepat 35. Program C ++ menunjukkan ini dengan baik karena Anda dapat mencampur ganda dan mengapung dan bermain dengan presisi dari angka floating point.


5
2018-02-08 00:56



Ini ada hubungannya dengan sifat aritmatika floating point yang tidak tepat. 3.5 % 0.1 membuat saya 0.099999999999999811, jadi Python berpikir bahwa 0,1 dibagi menjadi 3,5 paling banyak 34 kali, dengan sisa 0,099999999999999811. Saya tidak yakin apa algoritma yang digunakan untuk mencapai hasil ini, tapi itulah intinya.


1
2018-02-08 00:37