19

変数のすべての可能な値をfloat変数で正確に表すことができdoubleますか?

つまり、すべての可能な値に対してX、次のことが成功します。

float f1 = X;
double d = f1;
float f2 = (float)d;

if(f1 == f2)
  System.out.println("Success!");
else
  System.out.println("Failure!");

私の疑いは、例外がないか、例外がある場合はエッジケース(+/-無限大やNaNなど)の場合のみであるということです。

編集:質問の元の言い回しは紛らわしいものでした(2つの方法で述べられています。1つは「いいえ」と答えられ、もう1つは同じ答えに対して「はい」と答えられます)。質問のタイトルと一致するように言い換えました。

4

11 に答える 11

26

はい。

すべての可能なケースの列挙による証明:

public class TestDoubleFloat  {
    public static void main(String[] args) {
        for (long i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i++) {
            float f1 = Float.intBitsToFloat((int) i);
            double d = (double) f1;
            float f2 = (float) d;
            if (f1 != f2) {
                if (Float.isNaN(f1) && Float.isNaN(f2)) {
                    continue; // ok, NaN
                }
                fail("oops: " + f1 + " != " + f2);
            }
        }
    }
}

私のマシンでは12秒で終了します。32 ビットは小さいです。

于 2008-11-03T18:27:44.590 に答える
5

理論的には、そのような値はないので、「はい」、すべてのフロートはダブルとして表現可能である必要があります。フロートからダブルに変換するには、最後に00の4バイトをタックする必要があります。同じ形式ですが、フィールドのサイズが異なります。

于 2008-11-03T15:42:59.273 に答える
5

はい、floatはdoubleのサブセットです。floatとdoubleの両方の形式は(sign * a * 2 ^ b)です。floatとdoubleの違いは、aとbのビット数です。doubleはより多くのビットを使用できるため、float値をdoubleに割り当てることは、事実上、余分な0ビットを挿入することを意味します。

于 2008-11-03T15:43:01.647 に答える
3

誰もがすでに言ったように、「いいえ」。しかし、それは実際には質問自体に対して「はい」です。つまり、すべての floatはdouble として正確に表現できます。紛らわしい。:)

于 2008-11-03T15:45:49.970 に答える
3

私が言語仕様を正しく読んでいる場合 (そして他の誰もが確認しているように)、そのような値はありません。

つまり、それぞれが IEEE 754 標準値のみを保持すると主張しているため、2 つの間のキャストは、指定されたメモリ以外では変更されません。

(明確化: 値が float に保持できるほど小さい限り、変更はありません。明らかに、そもそも値が float に保持するにはビット数が多すぎる場合、double から float にキャストすると、精度が失われます。)

于 2008-11-03T15:50:39.430 に答える
1

@KenG: このコード:

float a = 0.1F
println "a=${a}"
double d = a
println "d=${d}"

0.1f を正確に表すことができないため、失敗しません。問題は、「double として表現できない float 値はありますか」というものでしたが、このコードでは証明されていません。0.1f を正確に格納することはできませんが、a に指定された値 (正確には 0.1f ではありません) は double として格納できます (これも正確には 0.1f ではありません)。Intel FPU を想定すると、a のビット パターンは次のようになります。

0 01111011 10011001100110011001101

d のビット パターンは次のとおりです。

0 01111111011 100110011001100110011010 (さらに多くのゼロが続きます)

同じ符号、指数 (どちらの場合も -4)、同じ小数部分 (上記のスペースで区切られている) を持ちます。出力の違いは、数値の 2 番目のゼロ以外の数字 (最初はポイントの後の 1) の位置によるもので、double でのみ表すことができます。文字列形式を出力するコードは、メモリに中間値を格納し、float と double に固有です (つまり、double-to-string 関数と別の float-to-string 関数があります)。to-string 関数が、FPU スタックを使用して to-string プロセスの中間結果を格納するように最適化されている場合、FPU は float と double の両方に同じより大きなフォーマット (80 ビット) を使用するため、出力は float と double で同じになります。とダブル。

double に同じように格納できない float 値はありません。つまり、float 値のセットは double 値のセットのサブセットです。

于 2008-11-03T17:28:31.823 に答える
0

Snark: NaN s は、変換後 (または実際には変換前) に異なる比較を行います。

ただし、これはすでに与えられた回答を無効にするものではありません。

于 2008-11-03T16:10:20.063 に答える
0

浮動小数点型が正確な値を表すと見なされる場合、他の投稿者が指摘したように、すべてのfloat値は として表現できますが、 で表現できるdoubleのは のいくつかの値のみです。一方、浮動小数点値が近似値であることを認識すると、実際の状況が逆転していることに気付くでしょう。非常に精密な器具を使用して 3.437mm を測定する場合、サイズは 3.4mm と正しく説明できます。定規を使用してオブジェクトを 3.4 mm と測定する場合、そのサイズを 3.400 mm と表すのは正しくありません。doublefloat

範囲の上部には、さらに大きな問題が存在します。float「計算値が 2^127 を未知数超えた」という値はありますが、doubleそれを示す値はありません。「無限大」を single から double にキャストすると、「計算された値が 2^1023 を未知の量だけ超えた」という値が得られます。

于 2012-08-28T03:49:53.433 に答える
0

理論的には、すべての通常のシングルは、指数と仮数をパディングして倍精度を作成し、パディングを削除して元のシングルに戻すことができます。

理論から現実に移行するとき、問題が発生します。あなたが理論や実装に興味を持っていたかどうかはわかりません。それが実装であれば、すぐにトラブルに巻き込まれる可能性があります。

IEEEは恐ろしいフォーマットです。私の理解では、IEEEは意図的に非常にタフに設計されているため、誰もそれに対応できず、市場がIntelに追いつくことができず(これは少し前のことです)、より多くの競争を可能にしています. それが本当なら、失敗しました。いずれにせよ、この恐ろしい仕様で立ち往生しています。TI 形式のようなものは、現実の世界では非常に多くの点ではるかに優れています。私はどちらの会社にも、これらの形式のいずれにも関係がありません。

この仕様のおかげで、実際に (ハードウェアまたはハードウェアとオペレーティング システムで) 満たす fpu はほとんどなく、次世代では失敗することがよくあります。(グーグル: TestFloat)。最近の問題は、上で指定したように、single から double および double から single ではなく、int から float および float から int にある傾向があります。もちろん、その変換を行うためにfpuが実行する操作は何ですか? 0 を追加しますか? 1倍?fpu とコンパイラに依存します。

上記の質問に関連するIEEEの問題は、すべての数値ではなく、多くの数値を表すことができる数ではなく、複数の方法があることです。コードを壊したい場合は、2 つの操作のいずれかでプラス ゼロに変換されることを期待して、マイナス ゼロから始めます。次に、デノーマルを試します。そして、それはシグナリング nan で失敗するはずですが、あなたはそれを既知の例外として呼び出しました。

問題は、等号です。これは浮動小数点数に関するルール 1 であり、等号は決して使用しないでください。Equals は値比較ではなくビット比較です。2 つの値が異なる方法で表されている場合 (プラス ゼロとマイナス ゼロなど)、同じ数であってもビット比較は失敗します。大なり小なりは fpu で行われ、等号は整数 alu で行われます。

問題を説明するために equal を使用した可能性があり、必ずしも成功または失敗したいコードを使用したとは限りません。

于 2008-11-04T15:48:13.227 に答える
0

あなたがリストしたコードを取り、C++ で試してみることにしました。実行が少し速くなり、安全でないキャストを行う方がはるかに簡単だと思ったからです。:-D

有効な数値の場合、変換が機能し、キャスト後に正確なビット単位の表現が得られることがわかりました。ただし、1.#QNAN0 などの非数値の場合、結果はソースの正確なビットではなく非数値の単純化された表現を使用します。例えば:

**** 失敗 **** 2140188725 | 1.#QNAN0 -- 0xa0000000 0x7ffa1606

unsigned int を float にキャストしてから double にキャストし、float に戻します。数値 2140188725 (0x7F90B035) は NAN になり、double への変換とその逆は NAN ですが、まったく同じ NAN ではありません。

簡単な C++ コードは次のとおりです。

typedef unsigned int uint;
for (uint i = 0; i < 0xFFFFFFFF; ++i)
{
    float f1 = *(float *)&i;
    double d = f1;
    float f2 = (float)d;
    if(f1 != f2)
        printf("**** FAILURE **** %u | %f -- 0x%08x 0x%08x\n", i, f1, f1, f2);
    if ((i % 1000000) == 0)
        printf("Iteration: %d\n", i);
}
于 2008-11-03T16:15:19.833 に答える
0

最初の質問への答えはイエスですが、「つまり」への答えはノーです。コード内のテストを変更しif (!(f1 != f2))て、2 番目の質問の答えが「はい」になるようにすると、すべての float 値に対して「成功」と出力されます。

于 2008-11-04T00:36:42.100 に答える