C ++でn番目の「nextafter」浮動小数点値を取得する標準的な方法はありますか


8

C ++にはstd::nextafter()があり、指定された浮動小数点値fの次の表現可能な値を返します。私の場合、下位仮数ビットにnビットのスロップを許可したいので、3ビットのスロップは、ある特定の値fの後に8番目の次の値を取得する必要があります。私はnextafter()8回電話することができますが、これを処理するより良い方法はありますか?

ほとんどの値について、IEEE 754のレイアウトのおかげで、FP値をにキャストしuint_64、許容誤差(1<<33ビットのスロップ)を追加してから、にキャストし直すことで問題を解決できますdouble。ただし、IEEE 754浮動小数点(良い仮定ですが、堅実でもありません)。

(背景として、これはFPの不正確さのためにサーフェスの内側に時々配置される光線とサーフェスの交点を巻き上げるために使用します。堅牢な浮動小数点に精通している人epsilonは、なぜ恐ろしい解決策であるかを理解します。)


次の8番目の値が正確に必要なようには思えません。f(正の場合)に1.00 ... 001を掛けても十分でしょうか?
Marc Glisse、

の値を使用して、std::numeric_limits<T>::is_iec559IEEE 754が使用されているかどうかを確認し、それに応じて関数を特殊化することもできます。
IlCapitano

回答:


2

単純なアプローチは、8回呼び出すのではなく、値と次の表現可能な浮動小数点の間の距離を8倍することです。 std::nextafter

double advance_float(double x, int d)
{
    double step = std::copysign((std::nextafter(x, x + d) - x) * d, d);
    return x + step;
}

ここにいくつかのテストがありますが、これがあなたのユースケースに適しているかどうかを判断するのはあなた次第です。

編集する

で述べたようにスティーブHollashxそのように大きなかもしれx + d == dDaniel Jourfrexp(およびldexp)を利用することを提案しましたが、次の試みでは、別のアプローチを使用して方向を決定します。

double advance_float(double x, int d)
{
    const double to = std::copysign(std::numeric_limits<double>::infinity(), d);
    const double next = std::nextafter(x, to);
    return x + std::copysign(d * (next - x), d);
}

それはstd::numeric_limits<double>::has_infinity == trueそれ以外::lowest()を想定していることに注意してください::max()

それらはいくつかの結果です

         xd前x次
-------------------------------------------------- ----------------------------------------
           1 1 0x1.fffffffffffffp-1 0x1p + 0 0x1.0000000000001p + 0
           1 8 0x1.ffffffffffff8p-1 0x1p + 0 0x1.0000000000008p + 0
     3.14159 8 0x1.921fb54442d1p + 1 0x1.921fb54442d18p + 1 0x1.921fb54442d2p + 1
      100.01 8 0x1.900a3d70a3d69p + 6 0x1.900a3d70a3d71p + 6 0x1.900a3d70a3d79p + 6
     -100.01 8 -0x1.900a3d70a3d79p + 6 -0x1.900a3d70a3d71p + 6 -0x1.900a3d70a3d69p + 6
       1e + 67 8 0x1.7bd29d1c87a11p + 222 0x1.7bd29d1c87a19p + 222 0x1.7bd29d1c87a21p + 222
       1e-59 8 0x1.011c2eaabe7dp-196 0x1.011c2eaabe7d8p-196 0x1.011c2eaabe7ep-196
           0 8 -0x0.0000000000008p-1022 0x0p + 0 0x0.0000000000008p-1022
4.94066e-324 8 -0x0.0000000000007p-1022 0x0.0000000000001p-1022 0x0.0000000000009p-1022

興味深いアプローチ。しかし、書かれているように、それx+dはあなたが探しているものではありません。xが大きい場合、(x + d)== xです。しかし、私はこのアイデアが好きです。一致する指数を使用して最下位ビットに等しい値を計算し、「slop」でスケーリングしてから、元の値に追加します。
Steve Hollasch

@SteveHollasch確かに、それnextafterが「方向」を渡す方法の問題です。
Bob__

frexpこれの前とldexp後で追加することはうまくいくでしょう、いいえ?
Daniel Jour

@DanielJourあなたが意味するか、これを?「うまくいった」ようですが、私は確かに多くのコーナーケースを見逃しています。
Bob__

素晴らしい。単なる解決策ではなく、気付かなかったたくさんの新しいツール。ではfrexp具体的には、私は世界を移動することができます。ありがとうございました!
Steve Hollasch

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.