Pythonでモジュロ演算子がどのように機能するかを誰かが説明できますか?理由がわかりません3.5 % 0.1 = 0.1
。
3 に答える
実際、それは真実で3.5 % 0.1
はありません0.1
。これは非常に簡単にテストできます。
>>> print(3.5 % 0.1)
0.1
>>> print(3.5 % 0.1 == 0.1)
False
実際には、ほとんどのシステムでは、3.5 % 0.1
です0.099999999999999811
。しかし、Pythonの一部のバージョンでは、str(0.099999999999999811)
次のようになり0.1
ます。
>>> 3.5 % 0.1
0.099999999999999811
>>> repr(3.5 % 0.1)
'0.099999999999999811'
>>> str(3.5 % 0.1)
'0.1'
さて、あなたはおそらくなぜ3.5 % 0.1
が0.099999999999999811
の代わりにあるのか疑問に思っているでしょう0.0
。これは、通常の浮動小数点の丸めの問題が原因です。すべてのコンピューター科学者が浮動小数点演算について知っておくべきことを読んでいない場合は、読む必要があります。少なくとも、この特定の問題に関するウィキペディアの簡単な要約を読んでください。
3.5/0.1
それはそうではないことにも注意してください34
、それは35
です。つまり、3.5/0.1 * 0.1 + 3.5%0.1
は、に3.5999999999999996
さえ近く3.5
ありません。これはモジュラスの定義のかなり基本的なことであり、Pythonや他のほぼすべてのプログラミング言語では間違っています。
しかし、Python3が救いの手を差し伸べます。知っているほとんどの人//
は、それが整数間の「整数除算」を行う方法であることを知っていますが、それが任意の型間のモジュラス互換除算を行う方法であることを認識していません。3.5//0.1
は34.0
、3.5//0.1 * 0.1 + 3.5%0.1
です(少なくともの小さな丸め誤差の範囲内で)3.5
。これは2.xにバックポートされているため、(正確なバージョンとプラットフォームによっては)これに依存できる場合があります。また、そうでない場合は、を使用できますdivmod(3.5, 0.1)
。これは、(丸め誤差の範囲内で)(34.0, 0.09999999999999981)
時間の霧の中に戻ります。もちろん、これはで(35.0, 0.0)
はなく(34.0, almost-0.1)
、まだ期待していましたが、丸め誤差のためにそれを行うことはできません。
簡単な修正を探している場合は、次のDecimal
タイプの使用を検討してください。
>>> 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')
これは魔法の万能薬ではありません。たとえば、演算の正確な値が基数10で有限に表現できない場合は常に丸め誤差に対処する必要がありますが、丸め誤差は人間の直感が予想する場合とよく一致します。問題になること。Decimal
(明示的な精度を指定したり、有効数字を追跡したりできるという利点もあります。またfloat
、2.4から3.3までのすべてのPythonバージョンで実際には同じですが、詳細float
は同時に2回変更されています。完全ではないというだけです。それは不可能だからです。)しかし、数値がすべて10進数で正確に表現可能であり、構成した精度よりも多くの桁を必要としないことが事前にわかっている場合は、機能します。
Moduloはrest
除算のを提供します。3.5
で割ると、残りの部分が表示され0.1
ます。ただし、浮動小数点数は2の累乗に基づいているため、数値は正確ではなく、丸め誤差が発生します。35
0
10進数の除算を正確にする必要がある場合は、10進数モジュールを使用してください。
>>> from decimal import Decimal
>>> Decimal('3.5') / Decimal('0.1')
Decimal('35')
>>> Decimal('3.5') % Decimal('0.1')
Decimal('0.0')
私の答えが誤解を招くと非難されているので、ここに全体の話があります:
Pythonフロート0.1
は10分の1よりわずかに大きいです:
>>> '%.50f' % 0.1
'0.10000000000000000555111512312578270211815834045410'
フロート3.5
をそのような数で割ると、残りはほぼになり0.1
ます。
数字から始めて、2桁0.11
の間にゼロを追加し続けて、を大きく保ちながら小さくします。1
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'
最後の行は、最終的に到達したという印象を与えます0.1
が、フォーマット文字列を変更すると、本当の性質が明らかになります。
>>> '%.20f' % (3.5 % 0.100000000001)
'0.09999999996600009156'
3.5 % 0.1 = 0.1
Pythonのデフォルトのfloat形式では、とが十分な精度を示さないだけ3.5 % 0.1 = 35.0
です。本当にそう3.5 % 0.100000... = 0.999999...
です3.5 / 0.100000... = 34.999999....
。除算の場合、最終的にはに切り上げられる正確な結果になります。34.9999...
35.0
おもしろい事実:より少し小さい数を使用し0.1
て同じ操作を実行すると、次よりわずかに大きい数になり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'
C ++を使用すると、floatで割った値が少し小さいことを示すこともでき3.5
ます。0.1
35
#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;
}
Pythonでは、丸め誤差が互いに打ち消し合うため3.5 / 0.1
、正確な結果が得られます。35
本当にです3.5 / 0.100000... = 34.9999999...
。そして34.9999...
、最終的には非常に長いので、最終的には正確になり35
ます。C ++プログラムは、doubleとfloatを組み合わせて、浮動小数点数の精度で遊ぶことができるため、これをうまく示しています。
これは、浮動小数点演算の不正確な性質と関係があります。Pythonは、0.1が最大34回3.5に分割され、0.099999999999999811が残っていると考えています3.5 % 0.1
。0.099999999999999811
この結果を達成するためにどのアルゴリズムが使用されているかは正確にはわかりませんが、それが要点です。