web-dev-qa-db-ja.com

Python floatsのモジュロ

Pythonでモジュロ演算子がどのように機能するかを説明できる人はいますか?理由を理解できない3.5 % 0.1 = 0.1

34
beruic

実際、_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_である理由を疑問に思っているでしょう。これは、通常の浮動小数点の丸めの問題によるものです。 すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと を読んでいない場合は、少なくともこの特定の問題の要約 Wikipedia を読むべきです。

また、_3.5/0.1_は_34_ではなく、_35_であることに注意してください。したがって、_3.5/0.1 * 0.1 + 3.5%0.1_は_3.5999999999999996_であり、3.5_に(close偶数ではありません。これは、モジュラスの定義のかなり基本的なものであり、Pythonでは間違いであり、他のすべてのプログラミング言語でも同様です。

しかし、Python 3が助けになります。_//_を知っているほとんどの人は、それが整数間の「整数除算」を行う方法であることを知っていますが、 anyタイプ間でモジュラス互換の除算を行う_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よりも利点があります。明示的な精度を指定したり、有効数字を追跡したりすることができ、すべてのPythonバージョン2.4から3.3、同時にfloatの詳細は2回変更されました。それは完全ではないというだけのことです。それは不可能だからです。)すべて10進数で正確に表現でき、構成した精度よりも多くの桁を必要としないため、機能します。

64
abarnert

Moduloは、除算のrestを提供します。 3.50.1で割ると、35が残り、0が残ります。しかし、floatは2の累乗に基づいているため、数値はexactではなく、丸め誤差が発生します。

10進数の除算を正確に行う必要がある場合は、decimalモジュールを使用します。

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

私の答えが誤解を招くものであると私はばかげているので、全体の物語が来ます:

0.10.1よりわずかに大きい

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

フロート3.5をそのような数で除算すると、残りのほぼ0.1が得られます。

0.11から始めて、2つの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'

python)のデフォルトの浮動小数点形式は、3.5 % 0.1 = 0.1および3.5 % 0.1 = 35.0。が実際に3.5 % 0.100000... = 0.999999...および3.5 / 0.100000... = 34.999999....になるほど十分な精度を表示しません。除算の場合、34.9999...は最終的に35.0に切り上げられるため、結果はexactになります。


面白い事実: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++を使用すると、3.5をfloatで割った0.135ではなく、少し小さいことを示すことさえできます。

#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

In Python 3.5 / 0.1は、丸めエラーが互いに相殺されるため、35の正確な結果を提供します。実際は3.5 / 0.100000... = 34.9999999...です。そして34.9999...は最終的に正確に35になります。C++プログラムは、doubleとfloatを混在させ、浮動小数点数の精度で再生できるため、これをうまく示しています。

7
bikeshedder

浮動小数点演算の不正確な性質に関係しています。 3.5 % 0.1取得します0.099999999999999811なので、Pythonは、0.1が最大34回3.5に分割され、0.099999999999999811が残っていると考えています。要旨。

1
jjlin