10

C89 では、floor() は double を返します。以下は動作することが保証されていますか?

double d = floor(3.0 + 0.5);
int x = (int) d;
assert(x == 3);

私の懸念は、floor の結果が IEEE 754 で正確に表現できない可能性があることです。したがって、d は 2.99999 のようになり、x は最終的に 2 になります。

この質問に対する答えが「はい」であるためには、int の範囲内のすべての整数が double として正確に表現可能でなければならず、floor は常にその正確に表現された値を返さなければなりません。

4

3 に答える 3

14

浮動小数点型が必要な仮数ビットをサポートしている場合、すべての整数は正確な浮動小数点表現を持つことができます。は仮数に 53 ビットを使用するためdouble、すべての 32 ビットints を正確に格納できます。結局のところ、指数がゼロの仮数として値を設定できます。

于 2009-01-13T18:37:08.257 に答える
3

floor() の結果が正確に表現できない場合、d の値は何になると思いますか? 確かに、変数に浮動小数点数の表現がある場合、定義により、それは正確に表現可能ですよね? あなたはdで表現を持っています...

(さらに、Mehrdad の答えは 32 ビットの int に対して正しいです。64 ビットの double64 ビットの int を使用するコンパイラでは、もちろんさらに多くの問題が発生します...)

編集:おそらく、「floor() の理論上の結果、つまり、引数以下の最大の整数値は、int として表現できない可能性がある」という意味でした。確かにそうですね。int が 32 ビットのシステムでこれを示す簡単な方法:

int max = 0x7fffffff;
double number = max;
number += 10.0;
double f = floor(number);
int oops = (int) f;

浮動小数点から整数への変換がオーバーフローしたときに C が何をするかは覚えていませんが、ここで起こります。

編集: 他にも考慮すべき興味深い状況があります。C# のコードと結果を次に示します。少なくともC でも同様のことが起こると思います。C# では、doubleは 64 ビットと定義されており、long.

using System;
class Test
{
    static void Main()
    {
        FloorSameInteger(long.MaxValue/2);
        FloorSameInteger(long.MaxValue-2);
    }

    static void FloorSameInteger(long original)
    {
        double convertedToDouble = original;
        double flooredToDouble = Math.Floor(convertedToDouble);
        long flooredToLong = (long) flooredToDouble;

        Console.WriteLine("Original value: {0}", original);
        Console.WriteLine("Converted to double: {0}", convertedToDouble);
        Console.WriteLine("Floored (as double): {0}", flooredToDouble);
        Console.WriteLine("Converted back to long: {0}", flooredToLong);
        Console.WriteLine();
    }
}

結果:

Original value: 4611686018427387903
Converted to double: 4.61168601842739E+18
Floored (as double): 4.61168601842739E+18
Converted back to long: 4611686018427387904

Original value: 9223372036854775805
Converted to double: 9.22337203685478E+18
Floored (as double): 9.22337203685478E+18
long に戻す: -9223372036854775808

言い換えると:

(long) floor((double) original)

と常に同じではありませんoriginal。これは驚くべきことではありません。(NaN 値を考えると) double よりも long 値の方が多く、多くの double は整数ではないため、すべての long が正確に表現できるとは期待できません。ただし、32 ビット整数すべてdouble として表現できます。

于 2009-01-13T18:41:27.623 に答える
2

何を聞きたいのか、少し混乱していると思います。floor(3 + 0.5)3、0.5、およびそれらの合計はすべて、実際の浮動小数点形式で正確に表現できるため、あまり良い例ではありません。floor(0.1 + 0.9)ここでの本当の問題は、 の結果が正確に表現できるかどうかではなく、呼び出しfloorの数値の不正確さが、すべての数値が正確であった場合に期待するものとは異なる戻り値になるかどうかです。この場合、答えはイエスだと思いますが、それは特定の数値に大きく依存します.floor

このアプローチが悪い場合は他の人に批判してもらいますが、可能な回避策の 1 つは、(1.0+0x1p-52)電話をかける前に自分の番号またはそれに類似したものを掛けることですfloor(おそらく を使用しnextafterた方がよいでしょう)。これにより、数値の最後の 2 進数の桁のエラーが原因で、正確に整数値ではなくわずかに下回った場合を補うことができますが、多くの操作で蓄積されたエラーは考慮されません。そのレベルの数値の安定性/正確さが必要な場合は、詳細な分析を行うか、数値を正しく処理できる任意精度または正確な数学ライブラリを使用する必要があります。

于 2010-08-11T09:30:07.157 に答える