9

Python で小さな数値を使って算術演算を行うと、次の予期しない結果が得られます。

>>> sys.float_info
sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)
>>> (1. - (1.e-17) ) < 1.
False

浮動小数点数が無限の精度を持っていないことは知っていますが、1e-17 のような「大きな」小さな数を処理できるはずですよね?

4

7 に答える 7

12

フロートの精度は、1よりも0の方が高くなります。

  • 0.0から0.1の間に4591870180066957722フロートがあります。
  • 0.9から1.0の間に900719925474099のフロートがあり、はるかに少ないです。

フロート密度は一定の間隔で半分になり、次のようになります。

フロートの密度の視覚化

これが、の次のフロートで、次1.の方向にあり0.ます。

>>> import math
>>> math.nextafter(1., 0.)
0.9999999999999999
>>> format(math.nextafter(1., 0.), ".32f")  # let's see more decimal places
'0.99999999999999988897769753748435'

1-10 -17の数学的に正しい値は0.99999999999999999(17の9があります)です。この番号をnと呼びます。ほとんどすべての数値と同様に、nを浮動小数点数で正確に表すことはできません。

0.99999999999999999                    # n
0.00000000000000001                    # distance between n and 1, i.e. 10^-17
0.00000000000000010102230246251565...  # distance between n and nextafter(1., 0.)

ご覧のとおり、1-10-17はからの約10倍の距離nextafter(1., 0.)です1.。式1. - 1.e-17がインタプリタによって評価されると、可能な限り最も近い結果が返されます。これは1.正確です。他のフロートを返すことは意味がありません。それは「実際の」結果からさらに遠くなります(しゃれを許してください)。

注: math.nextafter Python3.9以降で使用できます。numpy.nextafter以前のバージョンでは、同様に使用できます。

関連する質問-> Python浮動小数点値を可能な限り最小でインクリメントします

于 2012-05-11T17:10:05.837 に答える
7

epsilonまず、の戻り値に実際に含まれているものを確認しましょうsys.float_info

イプシロン(または) は、 0.5 + ≠ 0.5 AND 0.5 - ≠ 0.5となる最小の数です。

0.5Python は、繰り返しインクリメントまたはデクリメントを引き起こす最小の数値はepsilon=2.220446049250313e-16--であると言っていますが、これは値 0.5 のみです。をインクリメントしようとしてい1.0ます1.0e-17。これは、0.5 の場合 (1.0e-17 と 2.2e-16) よりも小さい数値でインクリメントされる大きな値 (1.0 対 0.5) です。1.0e-17 の増分値は 1.0 の相対イプシロンよりも 1 桁小さいため、おおよそ 1 桁ずれています。

これはここで見ることができます:

これらはの値を変更します0.5

>>> 0.5+sys.float_info.epsilon
0.5000000000000002
>>> 0.5-sys.float_info.epsilon
0.4999999999999998

これらの値は次のことを行いません。

>>> 0.5+sys.float_info.epsilon/10.0
0.5
>>> 0.5-sys.float_info.epsilon/10.0
0.5
>>> 5.0+sys.float_info.epsilon
5.0
>>> 5.0-sys.float_info.epsilon
5.0

説明:

IEEE 754は、ほとんどの標準的なコンピューターで現在使用されている浮動小数点形式を定義します (特殊なコンピューターやライブラリは別の形式を使用する場合があります)。IEEE 754 の64 ビット形式は、53 ビットの精度を使用して計算し、52 ビットを浮動小数点数の仮数に格納します。ポイント値。使用する固定の 52/53 ビットがあるため、仮数の大きさと精度は値が大きい/小さい場合に変化します。そのため、浮動小数点数の相対的な大きさが変化すると変化します。0.5 の値は、1.0 および 100.0 の値とは異なります。

さまざまな非常に優れたプラットフォーム固有の理由 (ストレージと表現、丸めなど) により、より小さな数値を使用することもできますが、epsilon は 64 ビット浮動小数点形式に対して 52 ビットの精度を使用するように定義されています。ほとんどの Python 実装では float に C の double float を使用するため、これを実証できます。

>>> 2**-52==sys.float_info.epsilon
True

プラットフォームが何ビットを実行するかを確認します。

>>> 0.5 + 2.0**-53
0.5000000000000001
>>> 0.5 - 2.0**-53
0.4999999999999999
>>> 0.5 + 2.0**-54
0.5                           # fail for 0.5 + 54 bits...
>>> 0.5 - 2.0**-54
0.49999999999999994           # OK for minus
>>> 0.5 - 2.0**-55
0.5                           # fail  for 0.5 minus 55 bits...

この問題にはいくつかの回避策があります。

  1. nextafterの C99 の概念を使用して、適切なイプシロンの値を計算できます。Python の場合、numpy または Decimal クラスを使用して を計算しますnextafternextafter私の以前の回答の詳細はこちら
  2. 整数を使用します。64 ビット整数は、丸めなしで 17 桁のイプシロン値を明確に処理します。
  3. 任意精度の数学ライブラリを使用します。Decimalは、標準の Python ディストリビューションに含まれています。

重要な概念は、 の値が値に対して相対的であるということです(また、インクリメントまたはデクリメントしている場合)。

これはここで見ることができます:

>>> numpy.nextafter(0.0,1.0)-0.0
4.9406564584124654e-324                 # a relative epsilon value of 4.94e-324
>>> numpy.nextafter(0.01,1.0)-0.01
1.7347234759768071e-18                  # 1e-17 would still work...
>>> numpy.nextafter(0.1,1.0)-0.1
1.3877787807814457e-17                  # 1e-17 would >>barely<< work...
>>> numpy.nextafter(0.5,1.0)-0.5
1.1102230246251565e-16                  # a relative epsilon value of 1.1e-16
>>> numpy.nextafter(500.0,501.0)-500.0
5.6843418860808015e-14                  # relative epsilon of 5.6e-14
>>> numpy.nextafter(1e17,1e18)-1e17
16.0                                    # the other end of the spectrum...

したがって、1e-17 は 0.0 から 0.1 の間の値をインクリメントするのに便利に機能しますが、それより大きな値は多くないことがわかります。上記のように、1e17 の相対値は 16 です。

于 2012-05-11T19:50:08.570 に答える
3

このレベルの精度が必要な場合は、Decimalモジュールを検討してください

>>> decimal.Decimal(1.0)-decimal.Decimal('1.0e-17')
Decimal('0.999999999999999990')
>>> decimal.Decimal(1.0)-decimal.Decimal('1.0e-17')<decimal.Decimal(1.0)
True

と:

>>> decimal.Decimal(1.0)-decimal.Decimal('1.0e-17')<1.0
True

ただし、変換エラーが発生する可能性があるため、最後のものには注意してください。

他の人は、すべてのコンピューター科学者が浮動小数点演算について知っておくべきことを提案しています。また、フロートに保存しないことをお勧めします

于 2012-05-11T17:09:41.843 に答える
2

それらを処理できます。ご了承ください

>>> 1.e-17 == 0
False

>>> 1.e-17 + 1.e-18
1.1e-17

仮数が有限精度に収まらないため、1-1e-17 を処理することはできません。

于 2012-05-11T17:07:52.047 に答える
0
>>> from decimal import Decimal
>>> Decimal('1')-Decimal(str(10**-17)) < Decimal('1')
True

そのような正確さのためには、 decimal モジュールを使用してください!

于 2012-05-11T17:12:41.727 に答える
0

あなたが持っているのでepsilon=2.220446049250313e-16、それは正常です。(1. - (1.e-17) ) = 11.e-17 < epsilon

于 2012-05-11T17:38:08.993 に答える