単純なアプローチは、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;
}
ここにいくつかのテストがありますが、これがあなたのユースケースに適しているかどうかを判断するのはあなた次第です。
編集する
で述べたようにスティーブHollash、x
そのように大きなかもしれx + d == d
。Daniel Jourはfrexp
(および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