9

フロート X があるとします。X よりも小さく、ロスレスでフロートに格納できる最大の数値を見つけたいとします。

IIRC の IEEE 標準では、float のビットを int 表現に変換し、1 を引いて、float に戻すことでこれを行うことができると述べています。

編集:これは、NaNまたはinfではない正の数に当てはまります。負の数の場合、追加する必要があります。詳細については、ローリングの回答を参照してください。)

表現を変更するには、切り捨てる C# の (キャスト) 演算子しか知りません。それは私が望むものではありません。

C#でこれを行う方法はありますか?

4

2 に答える 2

9

以下は、単純に afloatを に変換しint、変更してから、 a に戻す方法floatです。

float myFloat = 10.3f;
// Get the bytes making up the float
byte[] bytes = BitConverter.GetBytes(myFloat);
// Make an int out of them
int myInt = BitConverter.ToInt32(bytes, 0);
// Change it
myInt--;
// Get the bytes making up the int
bytes = BitConverter.GetBytes(myInt);
// Make a float out of them
myFloat = BitConverter.ToSingle(bytes, 0);
// gives 10.2999992 or so

BitConverter64ビットの同等物のためにこれが組み込まれています:

double myDouble = 10.3;
long myLong = BitConverter.DoubleToInt64Bits(myDouble);
myLong--;
myDouble = BitConverter.Int64BitsToDouble(myLong); // gives 10.2999999...

ただし、Peter Rudermanが指摘するように、基礎となる単純なデクリメントではint、次に小さい が確実に得られるわけではありませんfloat

特に、負の数の場合、浮動小数点数をより負にするために整数をインクリメントする必要があります。ゼロの場合、次に小さいものが実際には に対応するため、特別なケースが必要です。floatintNaN

一般的にこれらのケースに対処する必要があるいくつかの関数をまとめました。また、多数と正/負の無限大の間を賢明に移動しているようにも見えます! コードの長さを減らすために安全でない変換を使用しましたが、必要に応じて上記のバイト変換を使用できます。

static unsafe float Increment(float f)
{
    int val = *(int*)&f;
    if (f > 0)
        val++;
    else if (f < 0)
        val--;
    else if (f == 0)
        return float.Epsilon;
    return *(float*)&val;
}
static unsafe float Decrement(float f)
{
    int val = *(int*)&f;
    if (f > 0)
        val--;
    else if (f < 0)
        val++;
    else if (f == 0)
        return -float.Epsilon; // thanks to Sebastian Negraszus
    return *(float*)&val;
}

Jeppeが指摘しているように、おそらくあなたもしたいでしょう

  • 各関数をif (float.IsNaN(f)) return f;で開始して、誤って a をインクリメントまたはデクリメントして数値を指定しないようにしますNaN
  • float.PositiveInfinityまた、入力をorに対してチェックすることも検討して.NegativeInfinityください。数学的に言えば、これらはおそらくインクリメント/デクリメントの下で一定のままであるはずだからです。
于 2013-01-11T12:36:53.383 に答える
3

このためのライブラリ ルーチンがありますnexttowardf(x, -INFINITY);

独自のコードで実行したい場合は、以下に示すように (C で) ネイティブに (IEEE 754 浮動小数点演算で、浮動小数点エンコーディングまたはコンポーネントにアクセスせずに) 実行できます。

それぞれの場合に非正規値を使用するバージョン (プロセッサによっては遅い場合があります) と、入力が小さい場合にのみ非正規値を使用するバージョン (ただし分岐がある) の 2 つのバージョンが提供されています。+INFINITY は入力としてサポートされていませんが、簡単なテストでサポートを追加できます。これは 用doubleに書かれていますが、 の変更floatは簡単です。

が定義されている場合CompileMainは、テスト プログラムも含まれます。

#include <float.h>
#include <math.h>


/*  Return the next floating-point value before the finite value q.

    This was inspired by Algorithm 3.5 in Siegfried M. Rump, Takeshi Ogita, and
    Shin'ichi Oishi, "Accurate Floating-Point Summation", _Technical Report
    05.12_, Faculty for Information and Communication Sciences, Hamburg
    University of Technology, November 13, 2005.
*/
double NextBefore(double q)
{
    // SmallestPositive is the smallest positive floating-point number.
    static const double SmallestPositive = DBL_EPSILON * DBL_MIN;

    /*  Scale is .625 ULP, so multiplying it by any significand in [1, 2)
        yields something in [.625 ULP, 1.25 ULP].
    */
    static const double Scale = 0.625 * DBL_EPSILON;

#if 0
    /*  This version has a branch but uses subnormal values only if q is so
        small that q * Scale is subnormal.
    */
    double increment = fabs(q)*Scale;

    if (0. == increment)
        return q - SmallestPositive;

    return q - increment;
#else
    /*  This version uses a subnormal, SmallestPositive, in each case.
        This might cause poor performance on some processors.
    */
    return q - fmax(SmallestPositive, fabs(q)*Scale);
#endif
}


#if defined CompileMain


#include <stdio.h>
#include <stdlib.h>


#define NumberOf(a) (sizeof (a) / sizeof *(a))


int main(void)
{
    int status = EXIT_SUCCESS;

    static const struct { double in, out; } cases[] =
    {
        { -INFINITY,                -INFINITY                },
        { -0x1.fffffffffffffp1023,  -INFINITY                },
        { -0x1.ffffffffffffep1023,  -0x1.fffffffffffffp1023  },
        { -0x1.ffffffffffffdp1023,  -0x1.ffffffffffffep1023  },
        { -0x1.ffffffffffffcp1023,  -0x1.ffffffffffffdp1023  },
        { -0x1.0000000000003p1023,  -0x1.0000000000004p1023  },
        { -0x1.0000000000002p1023,  -0x1.0000000000003p1023  },
        { -0x1.0000000000001p1023,  -0x1.0000000000002p1023  },
        { -0x1.0000000000000p1023,  -0x1.0000000000001p1023  },

        { -0x1.fffffffffffffp1022,  -0x1.0000000000000p1023  },

        { -0x1.fffffffffffffp1,     -0x1.0000000000000p2     },
        { -0x1.ffffffffffffep1,     -0x1.fffffffffffffp1     },
        { -0x1.ffffffffffffdp1,     -0x1.ffffffffffffep1     },
        { -0x1.ffffffffffffcp1,     -0x1.ffffffffffffdp1     },
        { -0x1.0000000000003p1,     -0x1.0000000000004p1     },
        { -0x1.0000000000002p1,     -0x1.0000000000003p1     },
        { -0x1.0000000000001p1,     -0x1.0000000000002p1     },
        { -0x1.0000000000000p1,     -0x1.0000000000001p1     },

        { -0x1.fffffffffffffp-1022, -0x1.0000000000000p-1021 },
        { -0x1.ffffffffffffep-1022, -0x1.fffffffffffffp-1022 },
        { -0x1.ffffffffffffdp-1022, -0x1.ffffffffffffep-1022 },
        { -0x1.ffffffffffffcp-1022, -0x1.ffffffffffffdp-1022 },
        { -0x1.0000000000003p-1022, -0x1.0000000000004p-1022 },
        { -0x1.0000000000002p-1022, -0x1.0000000000003p-1022 },
        { -0x1.0000000000001p-1022, -0x1.0000000000002p-1022 },
        { -0x1.0000000000000p-1022, -0x1.0000000000001p-1022 },

        { -0x0.fffffffffffffp-1022, -0x1.0000000000000p-1022 },
        { -0x0.ffffffffffffep-1022, -0x0.fffffffffffffp-1022 },
        { -0x0.ffffffffffffdp-1022, -0x0.ffffffffffffep-1022 },
        { -0x0.ffffffffffffcp-1022, -0x0.ffffffffffffdp-1022 },
        { -0x0.0000000000003p-1022, -0x0.0000000000004p-1022 },
        { -0x0.0000000000002p-1022, -0x0.0000000000003p-1022 },
        { -0x0.0000000000001p-1022, -0x0.0000000000002p-1022 },
        { -0x0.0000000000000p-1022, -0x0.0000000000001p-1022 },

        { +0x1.fffffffffffffp1023,  +0x1.ffffffffffffep1023  },
        { +0x1.ffffffffffffep1023,  +0x1.ffffffffffffdp1023  },
        { +0x1.ffffffffffffdp1023,  +0x1.ffffffffffffcp1023  },
        { +0x1.0000000000004p1023,  +0x1.0000000000003p1023  },
        { +0x1.0000000000003p1023,  +0x1.0000000000002p1023  },
        { +0x1.0000000000002p1023,  +0x1.0000000000001p1023  },
        { +0x1.0000000000001p1023,  +0x1.0000000000000p1023  },

        { +0x1.0000000000000p1023,  +0x1.fffffffffffffp1022  },

        { +0x1.0000000000000p2,     +0x1.fffffffffffffp1     },
        { +0x1.fffffffffffffp1,     +0x1.ffffffffffffep1     },
        { +0x1.ffffffffffffep1,     +0x1.ffffffffffffdp1     },
        { +0x1.ffffffffffffdp1,     +0x1.ffffffffffffcp1     },
        { +0x1.0000000000004p1,     +0x1.0000000000003p1     },
        { +0x1.0000000000003p1,     +0x1.0000000000002p1     },
        { +0x1.0000000000002p1,     +0x1.0000000000001p1     },
        { +0x1.0000000000001p1,     +0x1.0000000000000p1     },

        { +0x1.0000000000000p-1021, +0x1.fffffffffffffp-1022 },
        { +0x1.fffffffffffffp-1022, +0x1.ffffffffffffep-1022 },
        { +0x1.ffffffffffffep-1022, +0x1.ffffffffffffdp-1022 },
        { +0x1.ffffffffffffdp-1022, +0x1.ffffffffffffcp-1022 },
        { +0x1.0000000000004p-1022, +0x1.0000000000003p-1022 },
        { +0x1.0000000000003p-1022, +0x1.0000000000002p-1022 },
        { +0x1.0000000000002p-1022, +0x1.0000000000001p-1022 },
        { +0x1.0000000000001p-1022, +0x1.0000000000000p-1022 },

        { +0x1.0000000000000p-1022, +0x0.fffffffffffffp-1022 },
        { +0x0.fffffffffffffp-1022, +0x0.ffffffffffffep-1022 },
        { +0x0.ffffffffffffep-1022, +0x0.ffffffffffffdp-1022 },
        { +0x0.ffffffffffffdp-1022, +0x0.ffffffffffffcp-1022 },
        { +0x0.0000000000004p-1022, +0x0.0000000000003p-1022 },
        { +0x0.0000000000003p-1022, +0x0.0000000000002p-1022 },
        { +0x0.0000000000002p-1022, +0x0.0000000000001p-1022 },
        { +0x0.0000000000001p-1022, +0x0.0000000000000p-1022 },
    };

    for (int i = 0; i < NumberOf(cases); ++i)
    {
        double in = cases[i].in, expected = cases[i].out;
        double observed = NextBefore(in);
        printf("NextBefore(%a) = %a.\n", in, observed);
        if (! (observed == expected))
        {
            printf("\tError, expected %a.\n", expected);
            status = EXIT_FAILURE;
        }
    }

    return status;
}


#endif  // defined CompileMain
于 2013-01-11T14:16:17.743 に答える