7

たとえば、IEEE-754 単精度でエンコードされた次の数値があります。

"0100 0001 1011 1110 1100 1100 1100 1100"  (approximately 23.85 in decimal)

上記の 2 進数はリテラル文字列に格納されます。

問題は、精度を失うことなく、この文字列を IEEE-754 倍精度表現 (次のようなものですが、値は同じではありません) に変換するにはどうすればよいですか?

"0100 0000 0011 0111 1101 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010"

これは同じ番号IEEE-754 倍精度でエンコードされます。

次のアルゴリズムを使用して、最初の文字列を最初に 10 進数に変換しようとしましたが、精度が失われます。

num in decimal = (sign) * (1 + frac * 2^(-23)) * 2^(exp - 127)

Windows プラットフォームで Qt C++ フレームワークを使用しています。

編集: 質問が明確に表現されていない可能性があります。つまり、真の値 23.85 がわからないということです。最初の文字列しか取得できず、精度を落とさずに倍精度表現に変換したいと考えています。

4

5 に答える 5

3

まあ:符号ビットを保持し、指数を書き直し(古いバイアスを差し引いて、新しいバイアスを加えたもの)、仮数の右側にゼロを埋めます...

(@Mark が言うように、いくつかの特殊なケース、つまりバイアス指数がゼロまたは最大の場合を個別に処理する必要があります。)

于 2012-09-17T20:44:33.427 に答える
2

IEEE-754(および一般に浮動小数点)は、周期的な2進数の小数を完全な精度で表すことはできません。実際、それらが比較的小さな整数の分子と分母を持つ有理数である場合でもそうではありません。一部の言語は、それを実行できる有理数型を提供します(これらは、無制限の高精度整数もサポートする言語です)。

結果として、あなたが投稿した2つの番号は同じ番号ではありません。

実際には次のとおりです。

10111.11011001100110011000000000000000000000000000000000000000 ...10111.11011001100110011001100110011001100110011001101000000000..。

ここで、はsの...無限シーケンスを表し0ます。

上記のコメントのStephenCanonは、対応する10進値を示しています(チェックしませんでしたが、彼が正しく設定したことを疑う理由はありません)。

したがって、単精度の数値には必要な情報がないため、実行したい変換を実行できません(数値が実際に周期的であるか、繰り返しがあるために単純にそうであるように見えるかを知る方法はありません)。 。

于 2012-09-17T21:09:23.060 に答える
2

まず、入力をバイナリで識別するための+1。

第二に、その数値は 23.85 を表していませんが、わずかに小さいです。最後の 2 進数を から0に反転する1と、数値は 23.85 を正確に表していませんが、わずかに多くなっています。これらの違いは、float では適切にキャプチャできませんが、double では近似的にキャプチャできます。

第三に、あなたが失っていると思うものは正確さではなく、正確さと呼ばれます. 数値の精度は、単精度から倍精度への変換によって常に増加しますが、変換によって精度が向上することはありません (不正確な数値は不正確なままですが、追加の精度によってより明白になります)。

数値を表示 (またはログ) する直前に、float に変換するか、丸めたり、非常に小さな値を追加することをお勧めします。これは、精度を上げることで視覚的な外観が失われるためです

キャストの直後に丸め、その後の計算で丸められた値を使用する誘惑に抵抗してください。これはループでは特に危険です。これにより、デバッガーの問題が修正されたように見えるかもしれませんが、累積された追加の不正確さが最終結果をさらに歪める可能性があります。

于 2012-09-17T20:49:29.537 に答える
1

文字列を実際の float に変換し、それを double に変換してから文字列に戻すのが最も簡単な場合があります。

于 2012-09-17T20:46:48.637 に答える
-1

一般に、2 進浮動小数点は、10 進数値を正確に表すことはできません。10 進小数値から 2 進浮動小数点への変換 (William D.Clinger による「How to Read Floating-Point Numbers Accurately」の「Bellerophon」を参照) および 2 進浮動小数点から 10 進値への変換 (「Dragon4」を参照) Guy L.Steele Jr. と Jon L.White による「How to Print Floating-Point Numbers Accurately」) は、10 進数を最も近い表現可能な 2 進浮動小数点に変換し、もう 1 つはエラーを制御して、元になった 10 進値 (両方のアルゴリズムが改善され、David Gay のdtoa.cでより実用的になりました。アルゴリズムは復元の基礎です。std::numeric_limits<T>::digits10type に格納されている浮動小数点値からの 10 進数 (場合によっては末尾のゼロを除く) T

残念ながら、値を大惨事に拡大する: 新しい数値をフォーマットしようとすると、多くの場合、元の 10 進数が生成されません。これは、ゼロで埋められたものが、作成される最も近い Bellerophon とは異なるためfloat、Dragon4 が期待するためです。ただし、基本的には2つのアプローチがあり、かなりうまく機能します。doublefloatdouble

  1. 誰かが提案したように、 を文字列に変換しfloat、この文字列を に変換しdoubleます。これは特に効率的ではありませんが、正しい結果が得られることを証明できます (もちろん、完全に自明ではないアルゴリズムが正しく実装されていることを前提としています)。
  2. 値が妥当な範囲内にあると仮定すると、10 の累乗を掛けて、10 進数の最下位桁が非ゼロになるようにし、この数値を整数に変換し、この整数を a に変換し、double最終的に結果の double を で割ることができます。元の10乗。これが正しい数値をもたらすという証拠はありませんが、興味のある値の範囲で正確に保存したいfloat場合、これは機能します。

この問題を完全に回避するための 1 つの合理的な方法は、最初にDecimal TRでC++ について説明したように、10 進浮動小数点値を使用することです。残念ながら、これらはまだ標準の一部ではありませんが、私はこれを変更するために C++ 標準化委員会に提案を提出しました。

于 2012-09-17T22:58:45.220 に答える