23

浮動小数点数の 2 つの異なる精度式に出くわしました。

⌊(N-1) log 10 (2)⌋ = 10 進数 6 桁(単精度)

N log 10 (2) ≒ 7.225 桁(単精度)

ここで、N = 24有効ビット(単精度)

最初の式は、 W. Kahan 教授によって書かれた「バイナリ浮動小数点演算の IEEE 標準 754」の 4 ページの上部にあります。

2 番目の式は、ウィキペディアの記事「単精度浮動小数点形式」のセクションIEEE 754 単精度バイナリ浮動小数点形式: binary32 にあります。

最初の式について、W. Kahan 教授は次のように述べています。

最大 6 sigの 10 進文字列の場合。12月 Single に変換されてから、同じ数の sig に戻されます。dec. の場合、最後の文字列は元の文字列と一致する必要があります。

2番目の式について、ウィキペディアは言う

...全体の精度は 24 ビットです (log 10 (2 24 ) ≈ 7.225 10 進数に相当)。

両方の式の結果 (10 進数の 6 桁と 7.225 桁) は異なりますが、どちらも浮動小数点バイナリに変換してから元に戻すことができる最上位の 10 進数を表すことを意図していると想定したため、同じであると予想しました。開始時と同じ有効桁数で 10 進数に変換します。

これらの 2 つの数値が異なるのはなぜですか? また、2 進数に変換して 10 進数に戻しても意味を失わない最上位の 10 進数の精度は何ですか?

4

3 に答える 3

15

これらは、わずかに異なる 2 つのことについて話しています。

7.225 1桁は、数値を内部に格納できる精度です。たとえば、倍精度数で計算を行った場合 (つまり、15 桁の精度で開始した場合)、それを単精度数に丸めた場合、その時点で残っていた精度はおおよそ次のようになります。 7桁。

6桁は、10進数の文字列から浮動小数点数への往復変換によって維持できる精度について話し、次に別の10進数の文字列に戻します

したがって、1.23456789文字列のような数値から始めて、それを float32 に変換し、結果を文字列に戻すと仮定しましょう。これを行うと、6 桁が正確に一致することが期待できます。ただし、7 桁目は丸められている可能性があるため、必ずしも一致するとは限りません (ただし、元の文字列の +/- 1 になる可能性があります。

たとえば、次のコードを考えてみましょう。

#include <iostream>
#include <iomanip>

int main() {
    double init = 987.23456789;
    for (int i = 0; i < 100; i++) {
        float f = init + i / 100.0;
        std::cout << std::setprecision(10) << std::setw(20) << f;
    }
}

これにより、次のようなテーブルが生成されます。

     987.2345581         987.2445679         987.2545776         987.2645874
     987.2745972         987.2845459         987.2945557         987.3045654
     987.3145752          987.324585         987.3345947         987.3445435
     987.3545532          987.364563         987.3745728         987.3845825
     987.3945923          987.404541         987.4145508         987.4245605
     987.4345703         987.4445801         987.4545898         987.4645386
     987.4745483         987.4845581         987.4945679         987.5045776
     987.5145874         987.5245972         987.5345459         987.5445557
     987.5545654         987.5645752          987.574585         987.5845947
     987.5945435         987.6045532          987.614563         987.6245728
     987.6345825         987.6445923          987.654541         987.6645508
     987.6745605         987.6845703         987.6945801         987.7045898
     987.7145386         987.7245483         987.7345581         987.7445679
     987.7545776         987.7645874         987.7745972         987.7845459
     987.7945557         987.8045654         987.8145752          987.824585
     987.8345947         987.8445435         987.8545532          987.864563
     987.8745728         987.8845825         987.8945923          987.904541
     987.9145508         987.9245605         987.9345703         987.9445801
     987.9545898         987.9645386         987.9745483         987.9845581
     987.9945679         988.0045776         988.0145874         988.0245972
     988.0345459         988.0445557         988.0545654         988.0645752
      988.074585         988.0845947         988.0945435         988.1045532
      988.114563         988.1245728         988.1345825         988.1445923
      988.154541         988.1645508         988.1745605         988.1845703
     988.1945801         988.2045898         988.2145386         988.2245483

これを調べると、最初の 6 桁の有効桁数が常に正確にパターンに従っていることがわかります (つまり、各結果は前の結果よりも正確に 0.01 大きい)。元の でわかるようにdouble、値は実際には 98x.xx456 ですが、単精度浮動小数点数を 10 進数に変換すると、7桁が頻繁に正しく読み取られないことがわかります。桁が 5 より大きい場合、98x.xx46 に切り上げますが、一部の値は切り上げません (たとえば、最初の列の最後から 2 番目の項目は で988.154541あり、切り上げではなく切り捨てになるため、 d の代わりに 98x.xx45 になります46. そのため、(保存された) 値が 7 桁 (プラス少し) の精度であっても、10 進数への変換とその逆の変換によって値を往復するまでには、その 7 桁目が正確に一致することに依存することはできません。より多く(十分な精度があるにもかかわらず、そうでない場合よりもはるかに頻繁になります)。


1. これは基本的に 7 桁を意味し、8桁目何もないよりは少し正確ですが、それほど多くはありません。そこから始まったものの約+/- .775になります(精度の桁がなければ、基本的にそこから始まったものの+/- 1になります)。 1.2345678.225.225

于 2015-06-06T23:48:37.300 に答える
2

まったく同じ式であることに注意してください。高校の数学の本のアイデンティティを思い出してください。

    Log(x^y) == y * Log(x)

実際に電卓で N = 24 の値を計算すると役立ちます。

  Kahan's:      23 * Log(2) = 6.924
  Wikipedia's:   Log(2^24)  = 7.225

Kahan は、floor() のせいで 6.924 を 6 桁に切り詰めることを余儀なくされました。唯一の実際の違いは、Kahan が 1 ビット少ない精度を使用したことです。

理由を推測するのはかなり難しいですが、教授は古いメモに頼っていたのかもしれません。IEEE-754 より前に書かれており、精度の 24 ビット目が無料であることを考慮していません。この形式はトリックを使用します。0 ではない浮動小数点値の最上位ビットは常に 1 です。したがって、格納する必要はありません。プロセッサは、計算を実行する前にそれを追加します。23 ビットの格納精度を 24 ビットの実効精度に変換します。

または、10 進文字列から 2 進浮動小数点値への変換自体がエラーを生成することを考慮に入れました。0.1 のような多くのナイス ラウンド 10 進値は、2 進値に完全に変換することはできません。10 進数の 1/3 のように、桁数は無限にあります。ただし、単純な丸めによって達成される +/- 0.5 ビットのオフの結果が生成されます。したがって、結果は 23.5 * Log(2) = 7.074 桁まで正確です。変換ルーチンがぎこちなく、適切に丸められないと仮定した場合、結果は +/-1 ビットずれる可能性があり、N-1 が適切です。彼らは不器用ではありません。

あるいは、彼は典型的な科学者や (天が禁じた) 会計士のように考え、計算の結果を 10 進数に戻すことも望んでいます。7 桁の 10 進数を自明に探したときに得られるようなもので、変換を行ったり来たりしても同じ数値は生成されません。はい、それは別の +/- 0.5 ビット エラーを追加し、合計で 1 ビット エラーになります。

しかし、絶対にその間違いを犯してはいけません。数値を操作することで得られるエラーを常に計算に含める必要があります。それらのいくつかは有効桁数をすぐに失います。特に引き算は非常に危険です。

于 2015-06-06T23:43:09.630 に答える