1

我有一个输入浮点值,它是 0.0f <= value < 1.0f(注意小于一)。

当将此值乘以更大的范围时,浮点精度自然会降低,这意味着该值可能会超出等效范围。

例如,如果我从以下值开始:

0.99999983534521f

然后乘以 100,我得到:

100.000000000000f

这很好,但是我如何将浮点表示减少为仍然小于 100 的最接近的浮点值?

我发现了这个小技巧:

union test
{
    int integer;
    float floating;
};

test value;

value.floating = 1.0f;

printf("%x\n", value.integer);

然后我取那个十六进制值并将它减少一个十六进制数字,然后像这样明确地设置它:

unsigned int almost_one = 0x3f7fffff;

float value = 1.0f;

if (value >= 1.0f)      std::memcpy(&value, &almost_one, sizeof(float));

这对于这个特定值很有效,但是我可以使用更通用的方法吗?

我希望有一个我不知道的魔法指令可以用来实现这一目标!

编辑:这里有一组很棒的答案,std::nextafter 看起来就像我所追求的。不幸的是,我还不能使用 C++11 数学库,所以这对我不起作用。为了避免复杂的事情,我将用 C++11 标记这个问题,并在下面接受 Mike 的回答。

我为 C++03 提出了一个新问题:C++11 的 std::nextafter 和 C++03 的 std::nexttoward 的替代方案?

4

2 回答 2

6

我希望有一个我不知道的魔法指令可以用来实现这一目标!

如果您有 C++11(或 C99)标准库,那么std::nextafter(value, 0.0f)from <cmath>(或nextafterfrom <math.h>)将为您提供小于value.

它在第一个参数之后,在第二个参数的方向上给出“下一个”不同的值;所以在这里,下一个不同的值更接近于零。

于 2013-05-01T16:51:08.300 回答
0

很抱歉造成混乱,我第一次错过了重点。您正在寻找的当然是最后一个单元(ULP),它与机器 epsilon密切相关。这是演示:

#include <iostream>
#include <cmath>
#include <cassert>

float compute_eps() {
  float eps = 1.0f;

  // Explicit cast to `float` is needed to
  // avoid possible extra precision pitfalls.
  while (float(1.0f + eps) != 1.0f)
    eps /= 2.0f;

  return eps;
}

float ulp(float x) {
  int exp;

  frexp(x, &exp);

  static float eps = compute_eps();

  return eps * (1 << exp);
}

main() {
  float x = 100.0f;
  float y = x - ulp(x);
  float z = nextafterf(x, 0.0f);

  assert(y == z);

  std::cout.precision(20);
  std::cout << y << std::endl;
  std::cout << z << std::endl;
}

请理解,这个答案更多是为了教育而不是实用。我想说明为了确定相邻的浮点数必须涉及哪些量(从理论上)。例如,当然可以使用std::numeric_limits<T>::epsilon()来确定机器 epsilon。或者继续使用防弹nextafterf(这可能比我的演示更有效地实现)直接获取相邻的浮点数。总而言之,不要太认真地判断这个答案。

注意:此演示中不处理指数的特殊情况(如NaNinfinitysubnormal等)。但是如何扩展此演示以支持它们非常简单。

于 2013-05-01T17:03:59.333 回答