web-dev-qa-db-ja.com

整数除算は常に通常の除算の下限に等しいですか?

大きな商の場合、整数除算(_//_)は必ずしも通常の除算の下限(math.floor(a/b))と等しいとは限りません。

Python docs( https://docs.python.org/3/reference/expressions.html -6.7)、

整数のフロア除算は整数になります。結果は、「床」関数を結果に適用した数学的除算の結果です。

しかしながら、

_math.floor(648705536316023400 / 7) = 92672219473717632

648705536316023400 // 7 = 92672219473717628
_

'{0:.10f}'.format(648705536316023400 / 7)は '92672219473717632.0000000000'を返しますが、小数部の最後の2桁は32ではなく28でなければなりません。

29
Aditya Chanana

テストケースの商が等しくない理由は、math.floor(a/b)ケースでは、結果が浮動小数点演算(IEEE-754 64ビット)で計算されるため、最大精度があることを意味します。あなたが持っている商は2より大きい53 上限を超えると、浮動小数点がユニットまで正確でなくなります。

ただし、整数除算では、Pythonはその無限の整数範囲を使用するため、結果は正しいです。

PEP 238の「真の分裂のセマンティクス」 も参照してください。

Intおよびlong引数の場合、真の除算は情報を失う可能性があることに注意してください。これは、真の分裂の性質にあります(合理性が言語にない限り)。意識的にlongを使用するアルゴリズムは、//、longの真の除算は53ビット以下の精度を保持するため(ほとんどのプラットフォームで)。

30
trincot

浮動小数点数として正確に表現するには大きすぎる整数値を扱っている可能性があります。数値は2 ^ 53よりも大幅に大きく、これは 隣接する浮動小数点double間のギャップが1より大きくなり始める です。そのため、浮動小数点除算を実行すると精度がいくらか失われます。

一方、整数の除算は正確に計算されます。

18
interfect

あなたの問題は、「/」が「真の除算演算子」と呼ばれることがあり、そのメソッド名が__truediv__、整数に対する動作は「真の数学的除算」ではありません。代わりに、必然的に精度が制限される浮動小数点の結果を生成します。

十分に大きな数の場合、数の整数部分でも浮動小数点の丸め誤差の影響を受ける可能性があります。 648705536316023400をPython float(IEEE double)に変換すると、648705536316023424に丸められます1

現在のPythonの組み込み型の演算子の正確な動作に関する正式なドキュメントは見つかりません。この機能を導入した元のPEPは、「/」は整数を浮動小数点に変換してから浮動小数点除算を実行することと同等であると述べています。ただし、Python 3.5での簡単なテストでは、そうではないことが示されています。その場合、次のコードは出力を生成しません。

for i in range(648705536316023400,648705536316123400):
    if math.floor(i/7) != math.floor(float(i)/7):
        print(i)

しかし、少なくとも私にとっては、出力を生成します。

代わりに、Pythonは提示された数値の除算を実行し、浮動小数点数に収まるように結果を丸めています。そのプログラム出力から例を挙げます。

648705536316123383 // 7                   == 92672219473731911
math.floor(648705536316123383 / 7)        == 92672219473731904
math.floor(float(648705536316123383) / 7) == 92672219473731920
int(float(92672219473731911))             == 92672219473731904

Python標準ライブラリはFraction型を提供し、intで除算されたFractionの除算演算子は「真の数学的除算」を実行します。

math.floor(Fraction(648705536316023400) / 7) == 92672219473717628
math.floor(Fraction(648705536316123383) / 7) == 92672219473731911

ただし、Fraction型を使用すると、潜在的に深刻なパフォーマンスとメモリへの影響に注意する必要があります。割合を大きくすることなくストレージ要件を増やすことができます。


「1つの丸めと2つの丸め」の理論をさらにテストするために、次のコードでテストを行いました。

#!/usr/bin/python3
from fractions import Fraction
edt = 0
eft = 0
base = 1000000000010000000000
top = base + 1000000
for i in range(base,top):
    ex = (Fraction(i)/7)
    di = (i/7)
    fl = (float(i)/7)
    ed = abs(ex-Fraction(di))
    ef = abs(ex-Fraction(fl))
    edt += ed
    eft += ef
print(edt/10000000000)
print(eft/10000000000)

また、最初にフロートに変換する場合よりも直接除算を実行する場合の方が、平均誤差の大きさは大幅に小さく、1丸め対2理論をサポートしています。

1フロートを直接印刷すると正確な値は表示されず、その値に丸められる最小の10進数が表示されることに注意してください(フロートからストリングへ、そしてフロートへのロスレスラウンドトリップ変換が可能です)。

9
plugwash