在 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<T>::epsilon()
来确定机器ε。或者继续使用防弹的nextafterf
(这可能比我的演示更有效地实现)直接获取相邻的浮点数。总而言之,不要太认真地判断这个答案。
注意: 指数的特殊情况(如 NaN、infinity、subnormal 等)是本演示中未处理。但如何扩展此演示以支持它们非常简单。
【讨论】:
从value
中减去以获得下一个较低可表示数字的数量称为 ULP,而不是 epsilon。
@Pascal Cuoq:看看here ULP 与机器 epsilon 的关系。
我知道 ULP 与 epsilon 的关系,我只是说 OP 想要获得最接近 100 的值的下浮点数,减去机器 epsilon 是不会得到的他在那里。
表达式1.0f + eps != 1.0f
可能会失败; C++ 标准允许实现使用额外的精度,有些实现确实如此。
@Eric:如果有人感兴趣,我们已经讨论过这个问题here。以上是关于在 C++ 中找到小于特定整数值的最接近的浮点值?的主要内容,如果未能解决你的问题,请参考以下文章
写出一个程序,接受一个正浮点数值,输出该数值的近似整数值。如果小数点后数值大于等于5,向上取整;小于5,则向下取整。