Pythonで1 // 0.01 == 99なのはなぜですか?


31

これは古典的な浮動小数点精度の問題だと思いますが1//0.01、Python 3.7.5 で実行すると、この結果に頭を悩ませようとしています99

期待どおりの結果だと思いますが、使用int(1/f)する方が安全1//fかどうかを判断する方法はありますか?


4
はい、常により安全なint(1 / f)です。//がFLOOR除算であり、ROUNDと誤って考えるためです。
Perdi Estaquel


2
重複ではありません。これは、常に使用しround()、使用しない//か、または使用することにより、99.99%の期待どおりに機能しint()ます。リンクされた質問は、浮動小数点数の比較については切り捨てとは関係がなく、そのような簡単な修正はありません。
最大

回答:


23

これが実数で除算された場合、1//0.01正確に100になります。ただし、これらは浮動小数点近似であるため、0.011/100よりわずかに大きく、商は100よりわずかに小さくなります。これは、99の値です。 99に。


3
これは、「いつ安全かを判断する方法はあるか」という部分には対応していません。
スコットハンター

10
「安全」は明確に定義されていません。
chepner

1
それを完全に無視するのに十分、特に。OPが浮動小数点の問題を認識している場合
スコットハンター

3
@chepner「より安全」が明確に定義されていない場合は、説明を求めるのがよいでしょう:/

2
「より安全」とは「安い電卓よりも悪くないエラー」を意味することは私にはかなり明白です
最大の

9

この結果の理由は、あなたが述べているようなものであり、浮動小数点演算は壊れていますか?および他の多くの同様のQ&A。

分子と分母の小数の数がわかっている場合、より信頼性の高い方法は、最初にこれらの数値を乗算して整数として処理できるようにし、次に整数除算を実行することです。

したがって、あなたのケースで1//0.01は最初に1*100//(0.01*100)100に変換する必要があります。

さらに極端な場合でも、「予期しない」結果が得られる可能性があります。round整数除算を実行する前に、分子と分母の呼び出しを追加する必要がある場合があります。

1 * 100000000000 // round(0.00000000001 * 100000000000)

ただし、これが固定小数点(金額、セント)の処理に関するものである場合は、セントをunitとして処理することを検討してください。これにより、すべての演算を整数演算として実行できます。時にメインの通貨単位(ドル)のみ変換 I / O。

あるいは、decimalのような10進数用のライブラリを使用します。

...正しく丸められた高速の10進浮動小数点演算をサポートします。

from decimal import Decimal
cent = Decimal(1) / Decimal(100) # Contrary to floating point, this is exactly 0.01
print (Decimal(1) // cent) # 100

3
「明らかに100です。」必ずしもそうとは限りません。.01が正確でない場合、.01 * 100も正確ではありません。手動で「調整」する必要があります。
glglgl

8

あなたが考慮しなければならないの//は、それがfloor演算子であるため、最初に100に落ちる確率が99と同じであるかのように考える必要があります(*)(操作は正確に100.00を得る可能性がある場合100 ± epsilonepsilon>0行われるため)..0は非常に低いです。)

同じことがマイナス記号で確認できます

>>> 1//.01
99.0
>>> -1//.01
-100.0

そして、あなたは(驚かないように)驚かれるはずです。

一方、 int(-1/.01)最初に除算を実行してから、int()inを適用します。これは、ではなく0への切り捨てです!つまり、その場合、

>>> 1/.01
100.0
>>> -1/.01
-100.0

したがって、

>>> int(1/.01)
100
>>> int(-1/.01)
-100

ただし、四捨五入すると、この演算子の期待どおりの結果が得られます。これも、これらの数値では誤差が小さいためです。

(*)確率が同じであると言っているのではなく、浮動小数点演算を使用してそのような計算を実行するとき、得られているものの推定値であるということを単にアプリオリと言っているだけです。


7

浮動小数点数はほとんどの10進数を正確に表すことができないため、浮動小数点リテラルを入力すると、実際にはそのリテラルの概算が得られます。入力した数値よりも近似値が大きくなる場合と小さくなる場合があります。

浮動小数点数の正確な値は、それをDecimalまたはFractionにキャストすることで確認できます。

>>> from decimal import Decimal
>>> Decimal(0.01)
Decimal('0.01000000000000000020816681711721685132943093776702880859375')
>>> from fractions import Fractio
>>> Fraction(0.01)
Fraction(5764607523034235, 576460752303423488) 

Fraction型を使用して、不正確なリテラルによって引き起こされたエラーを見つけることができます。

>>> float((Fraction(1)/Fraction(0.01)) - 100)
-2.0816681711721685e-15

また、numpyからnextafterを使用することにより、100前後の粒度の倍精度浮動小数点数がどの程度かを確認することもできます。

>>> from numpy import nextafter
>>> nextafter(100,0)-100
-1.4210854715202004e-14

これから、に最も近い浮動小数点数1/0.01000000000000000020816681711721685132943093776702880859375は実際には100であると推測できます。

違い1//0.01とはint(1/0.01)丸めです。1 // 0.01は、正確な結果を1つのステップで次の整数に切り捨てます。したがって、結果は99になります。

一方、int(1 / 0.01)は2段階で丸めます。最初に結果を最も近い倍精度浮動小数点数(正確には100)に丸め、次にその浮動小数点数を次の整数(これは再び正確に100)。


これを丸めと呼ぶのは誤解を招きやすいです。切り捨てまたはゼロへの丸めのいずれかで呼び出す必要がありますint(0.9) == 0およびint(-0.9) == 0
最大

これは、ここで話している2進浮動小数点型です。(10進浮動小数点型もあります。)
Stephen C

3

以下を実行すると

from decimal import *

num = Decimal(1) / Decimal(0.01)
print(num)

出力は次のようになります。

99.99999999999999791833182883

これを切り捨てて、それが内部的に表されます方法ですが//得られます99


2
この場合はエラーを表示するのに十分正確ですが、「10進数」の計算も正確ではないことに注意してください。
プラグウォッシュ

ではDecimal(0.01)あなたは手遅れです、エラーはすでにあなたが呼び出す前にこっそりDecimal。これが質問への答えであるかどうかはわかりません... Decimal(1) / Decimal(100)私が私の答えで示したように、最初にで正確な0.01を計算する必要があります。
トリンコット

@trincot私の答えは、「なぜ1 // 0.01 == 99」というタイトルの質問に対するものです。浮動小数点数が内部的にどのように扱われるかをOPに見せようとしました。
雨の
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.