12

ここでボンネットの下で何が起こっているのか、誰かが開梱するのを手伝ってくれますか?

>>> 1e16 + 1.
1e+16
>>> 1e16 + 1.1
1.0000000000000002e+16

私は64ビットのPython 2.7を使用しています。まず、float の精度は 15 しかないので、丸め誤差だと思います。本当の浮動小数点の答えは次のようなものかもしれません

10000000000000000.999999....

そして、小数は取り除かれます。しかし、2 番目の結果は、この理解に疑問を抱かせます。1 を正確に表すことはできませんか? 何かご意見は?

[編集:明確にするために。答えが「間違っている」と言っているわけではありません。明らかに、彼らは正しいです。理由を理解しようとしているだけです。]

4

4 に答える 4

10

できるだけ近くで丸めています。

フローティングヘクスの1e16は0x4341c37937e08000

1e16+2は0x4341c37937e08001です。

このレベルの大きさでは、表現できる精度の最小の差は2です。1.0を追加すると正確に切り捨てられます(通常、IEEE浮動小数点演算は偶数に丸められるため)。1.0より大きい値を追加すると、次の表現可能な値に切り上げられます。

于 2013-02-25T03:56:53.467 に答える
5

10^16 = 0x002386f26fc10000 は倍精度浮動小数点数として正確に表現できます。次に表現可能な数は 1e16+2 です。1e16+1 は 1e16 に正しく丸められ、1e16+1.1 は 1e16+2 に正しく丸められます。この C プログラムの出力を確認します。

#include <stdio.h>
#include <math.h>
#include <stdint.h>

int main()
{
  uint64_t i = 10000000000000000ULL;
  double a = (double)i;
  double b = nextafter(a,1.0e20); // next representable number
  printf("I=0x%016llx\n",i); // 10^16 in hex
  printf("A=%a (%.4f)\n",a,a); // double representation
  printf("B=%a (%.4f)\n",b,b); // next double
}

出力:

I=0x002386f26fc10000
A=0x1.1c37937e08p+53 (10000000000000000.0000)
B=0x1.1c37937e08001p+53 (10000000000000002.0000)
于 2013-02-25T04:01:00.247 に答える
4

いくつかのフロートをデコードして、実際に何が起こっているか見てみましょう! Common Lisp を使用します。Common Lisp には、浮動小数点数の有効桁数 (別名仮数) と指数を取得するための便利な関数があり、ビットをいじる必要はありません。使用されるすべての浮動小数点数は、IEEE 倍精度浮動小数点数です。

> (integer-decode-float 1.0d0)
4503599627370496
-52
1

つまり、有意桁に格納されている値を整数と見なすと、使用可能な最大 2 のべき乗 (4503599627370496 = 2^52) を縮小 (2^-52) したものになります。(仮数の左側にゼロを持たない方が簡単なため、指数が 0 の 1 として格納されません。これにより、左端の 1 ビットの表現をスキップして精度を上げることができます。この形式ではない数値は呼び出されます。デノーマル。)

1e16 を見てみましょう。

> (integer-decode-float 1d16)
5000000000000000
1
1

ここに、(5000000000000000) * 2^1 という表現があります。有効桁数は丸められた 10 進数ですが、2 のべき乗ではないことに注意してください。これは、1e16 が 2 の累乗ではないためです。10 を掛けるたびに、2 と 5 を掛けていることになります。2 を掛けることは指数を増やすだけですが、5 を掛けることは「実際の」掛け算であり、ここでは 5 を 16 回掛けています。

5000000000000000 = 10001110000110111100100110111111000001000000000000000 (base 2)

double float には 53 ビットの有意桁があるため、これが 53 ビットの 2 進数であることに注意してください。

しかし、この状況を理解するための鍵は、指数が 1 であることです (指数が小さいということは、精度の限界に近づいていることを示しています)。これは、float 値がこの仮数の 2^1 = 2 倍であることを意味します。

では、この数に 1 を足すとどうなるでしょうか? さて、同じスケールで 1 を表す必要があります。しかし、仮数の最下位ビットの値が 2 であるため、この数値に加えることができる最小の変更は正確に 2 です。

つまり、仮数をインクリメントして可能な限り小さな変更を行うと、次のようになります。

5000000000000001 = 10001110000110111100100110111111000001000000000000001 (base 2)

指数を適用すると、2 * 5000000000000001 = 1000000000000002 が得られます。これは、観察した値とまったく同じです。10000000000000000 または 10000000000000002 のいずれかのみを持つことができ、10000000000000001.1 は後者に近いです。

(ここでの問題は、10 進数が 2 進数で正確ではないということでさえないことに注意してください! ここには 2 進数の「10 進数の繰り返し」はなく、有効桁数の右端にたくさんの 0 ビットがあります。入力がきちんと落ちるだけです。最下位ビットのすぐ上。)

于 2013-02-25T04:13:38.580 に答える
3

numpyを使用すると、次に大きい、または小さい表現可能なIEEE浮動小数点数を確認できます。

>>> import numpy as np
>>> huge=1e100
>>> tiny=1e-100
>>> np.nextafter(1e16,huge)
10000000000000002.0
>>> np.nextafter(1e16,tiny)
9999999999999998.0

それで:

>>> (np.nextafter(1e16,huge)-np.nextafter(1e16,tiny))/2.0
2.0

と:

>>> 1.1>2.0/2
True

したがって、1e16 + 1.1は、次に大きいIEEE表現可能な数値10000000000000002.0に正しく丸められます。

そのまま:

>>> 1e16+1.0000000000000005
1.0000000000000002e+16

1e16-(1より少し大きいもの)は2で切り捨てられ、次に小さいIEEE番号になります。

>>> 1e16-1.0000000000000005
9999999999999998.0

32ビットと64ビットのPythonは無関係であることに注意してください。重要なのは、使用されるIEEE形式のサイズです。また、数値の大きさが大きいほど、イプシロン値(基本的に次に大きい2つのIEEE値と小さいIEEE値の間の広がり)が変化することにも注意してください。

これも少しずつ見ることができます:

>>> def f_to_bits(f): return struct.unpack('<Q', struct.pack('<d', f))[0]
... 
>>> def bits_to_f(bits): return struct.unpack('<d', struct.pack('<Q', bits))[0]
... 
>>> bits_to_f(f_to_bits(1e16)+1)
1.0000000000000002e+16
>>> bits_to_f(f_to_bits(1e16)-1)
9999999999999998.0
于 2013-02-25T04:32:42.760 に答える