在 C++ 中找到小于特定整数值的最接近的浮点值?

Posted

技术标签:

【中文标题】在 C++ 中找到小于特定整数值的最接近的浮点值?【英文标题】:Finding the closest floating point value less than a specific integer value in C++? 【发布时间】:2013-05-01 16:42:07 【问题描述】:

我有一个输入浮点值,它是 0.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 提出了一个新问题:Alternative to C++11's std::nextafter and std::nexttoward for C++03?

【问题讨论】:

这看起来像是未定义的行为...... 你想达到什么目的?如果您的结果正好是 100.0f,那么这是最接近实际结果的数字。将尾数减一只会使您的结果不那么准确。 你从0.99999983534521f 得到 100.0,因为它实际上是 1.0,因为那里有足够的 9 和 8 来翻转它 - 它的准确度并不低,这正是数字的限制是(如果你打印原始数字,你会得到 1.0,因为这是它的值)。请记住,浮点数只有 24 位尾数,以十进制形式给出大约 7 位有效数字。乘以一个数字不会降低其精度。浮点数在加减较大数字时会丢失精度,因为数字必须归一化[小数点在同一位置]。 @MatsPetersson:不,最接近 0.99999983534521 的 IEEE-754 32 位二进制浮点值是 0.999999821186065673828125,在您到达 1 之前还有两个。 @MatsPetersson:我没有看到 Pascal Cuoq 说问题中描述的问题不会发生。他写道x*M > M 不可能是真的。但是,问题是x*M == M 可能是真的。 【参考方案1】:

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

如果你有一个 C++11(或 C99)标准库,那么来自<cmath>std::nextafter(value, 0.0f)(或来自<math.h>nextafter)将为你提供小于value 的最大可表示值.

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

【讨论】:

如果value 是正数,它会为您提供小于value 的最大可表示值。这在问题中描述的情况下是令人满意的。但是,如果您想要所有情况下的下一个最大表示值,它应该是std::nextafter(value, -std::numeric_limits<float>().infinity()) @EricPostpischil:确实,“更小”是指“更小的量级”,并添加了第二段以澄清我的意思是“接近零”。除非我需要,否则我会有点紧张地搞乱无穷大,所以我没有提到这个问题,“趋向零”就足够了。【参考方案2】:

对不起,我第一次错过了重点。你要找的当然是unit in the last place (ULP),它与machine 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&lt;T&gt;::epsilon() 来确定机器ε。或者继续使用防弹的nextafterf(这可能比我的演示更有效地实现)直接获取相邻的浮点数。总而言之,不要太认真地判断这个答案。

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

【讨论】:

value 中减去以获得下一个较低可表示数字的数量称为 ULP,而不是 epsilon。 @Pascal Cuoq:看看here ULP 与机器 epsilon 的关系。 我知道 ULP 与 epsilon 的关系,我只是说 OP 想要获得最接近 100 的值的下浮点数,减去机器 epsilon 是不会得到的他在那里。 表达式1.0f + eps != 1.0f可能会失败; C++ 标准允许实现使用额外的精度,有些实现确实如此。 @Eric:如果有人感兴趣,我们已经讨论过这个问题here。

以上是关于在 C++ 中找到小于特定整数值的最接近的浮点值?的主要内容,如果未能解决你的问题,请参考以下文章

写出一个程序,接受一个正浮点数值,输出该数值的近似整数值。如果小数点后数值大于等于5,向上取整;小于5,则向下取整。

从 C++ 中的字符串获取整数值

在 c++ 中可以容纳啥最大整数,能够将浮点类型加 1?

python:在大整数列表中查找小整数列表的最接近匹配

OpenCV3cvRound()cvFloor()cvCeil()函数详解

在Graph中找到仅通过小于或等于一次的特定边的最短路径