26

単体テストの一環として、いくつかの境界条件をテストする必要があります。1 つのメソッドは引数を受け入れSystem.Doubleます。

次に小さいdouble 値を取得する方法はありますか? (つまり、仮数を 1 単位値だけ減らす)?

使用を検討しDouble.Epsilonましたが、これはゼロからの最小のデルタにすぎないため信頼性が低く、より大きな値 (つまり9999999999 - Double.Epsilon == 9999999999) では機能しません。

では、必要なアルゴリズムまたはコードは次のとおりです。

NextSmallest(Double d) < d

...常に真です。

4

2 に答える 2

22

BitConverter数が有限である場合、クラスでいくつかの便利なメソッドを使用できます。

long bits = BitConverter.DoubleToInt64Bits(value);
if (value > 0)
    return BitConverter.Int64BitsToDouble(bits - 1);
else if (value < 0)
    return BitConverter.Int64BitsToDouble(bits + 1);
else
    return -double.Epsilon;

IEEE-754 形式は、指数と仮数を構成するビットが一緒になって、浮動小数点数と同じ順序の整数を形成するように設計されています。したがって、最大の小さい数値を取得するには、値が正の場合はこの数値から 1 を引き、値が負の場合は 1 を加算します。

これが機能する主な理由は、仮数の先頭ビットが格納されていないことです。仮数がすべてゼロの場合、数値は 2 のべき乗です。指数/仮数の組み合わせから 1 を引くと、すべて 1 になり、指数ビットから借りる必要があります。つまり、指数をデクリメントする必要があります。これはまさに私たちが望んでいることです。

于 2013-03-11T05:51:28.050 に答える
2

倍精度浮動小数点に関するウィキペディアのページは次のとおりです: http://en.wikipedia.org/wiki/Double_precision_floating-point_format

double楽しみのために、形式のバイナリ表現を分解し、仮数を減らし、結果の double を再構成するコードをいくつか書きました。仮数に暗黙のビットがあるため、それを確認し、それに応じて指数を変更する必要があり、限界近くで失敗する可能性があります。

コードは次のとおりです。

public static double PrevDouble(double src)
{
    // check for special values:
    if (double.IsInfinity(src) || double.IsNaN(src))
        return src;
    if (src == 0)
        return -double.MinValue;

    // get bytes from double
    byte[] srcbytes = System.BitConverter.GetBytes(src);

    // extract components
    byte sign = (byte)(srcbytes[7] & 0x80);
    ulong exp = ((((ulong)srcbytes[7]) & 0x7F) << 4) + (((ulong)srcbytes[6] >> 4) & 0x0F);
    ulong mant = ((ulong)1 << 52) | (((ulong)srcbytes[6] & 0x0F) << 48) | (((ulong)srcbytes[5]) << 40) | (((ulong)srcbytes[4]) << 32) | (((ulong)srcbytes[3]) << 24) | (((ulong)srcbytes[2]) << 16) | (((ulong)srcbytes[1]) << 8) | ((ulong)srcbytes[0]);

    // decrement mantissa
    --mant;

    // check if implied bit has been removed and shift if so
    if ((mant & ((ulong)1 << 52)) == 0)
    {
        mant <<= 1;
        exp--;
    }

    // build byte representation of modified value
    byte[] bytes = new byte[8];
    bytes[7] = (byte)((ulong)sign | ((exp >> 4) & 0x7F));
    bytes[6] = (byte)((((ulong)exp & 0x0F) << 4) | ((mant >> 48) & 0x0F));
    bytes[5] = (byte)((mant >> 40) & 0xFF);
    bytes[4] = (byte)((mant >> 32) & 0xFF);
    bytes[3] = (byte)((mant >> 24) & 0xFF);
    bytes[2] = (byte)((mant >> 16) & 0xFF);
    bytes[1] = (byte)((mant >> 8) & 0xFF);
    bytes[0] = (byte)(mant & 0xFF);

    // convert back to double and return
    double res = System.BitConverter.ToDouble(bytes, 0);
    return res;
}

これらはすべて、仮数の最下位ビットの変更によって初期値とは異なる値を提供します...理論的には:)

ここにテストがあります:

public static Main(string[] args)
{
    double test = 1.0/3;
    double prev = PrevDouble(test);
    Console.WriteLine("{0:r}, {1:r}, {2:r}", test, prev, test - prev);
}

私のPCで次の結果が得られます。

0.33333333333333331, 0.33333333333333326, 5.5511151231257827E-17

違いはありますが、おそらく丸めしきい値を下回っています。ただし、式test == prevはfalseと評価され、上記のように実際の違いがあります:)

于 2013-03-11T05:23:53.473 に答える