float
(a.k.a。single)值是一个4字节的值,并且应该表示任何实数值。由于它的格式化方式和有限的字节数,它可以表示最小值和最大值,并且它具有有限的精度,具体取决于它自己的值。
我想知道是否有一种方法可以获得高于或低于某个参考值的最接近的可能值,给定浮点数的有限精度。对于整数,这是微不足道的:只需添加或减去1.但是使用float
,您不能简单地添加或减去最小浮点值并期望它与原始值不同。即。
float FindNearestSmaller (const float a)
{
return a - FLT_MIN; /* This doesn't necessarily work */
}
事实上,上述几乎永远不会奏效。在上述情况下,返回通常仍然等于a
,因为FLT_MIN
远远超出a
的精度。你可以轻松地自己试试这个:它适用于例如0.0f
,或订单数量非常少FLT_MIN
,但不包括0到100之间的任何订单。
那么在给定浮点精度的情况下,如何获得最接近但小于或大于a
的值?
注意:虽然我主要对C / C ++答案感兴趣,但我认为答案适用于大多数编程语言。
答案 0 :(得分:14)
查找浮点值邻居的标准方法是nextafter
的函数double
和nextafterf
的{{1}}。第二个论点给出了方向。请记住,无穷大是IEEE 754浮点数中的合法值,因此您可以调用float
来获得nextafter(x, +1.0/0.0)
之上的值,这对于x
也是如此(如果你写了DBL_MAX
,申请nextafter(x, DBL_MAX)
时会返回DBL_MAX
。
有时有用的两种非标准方式是:
访问x == DBL_MAX
/ float
的表示形式,作为相同大小的无符号整数,并递增或递减此整数。浮点格式是经过精心设计的,因此对于正浮点数和负浮点数,表示的位(看作整数)与所表示的浮点单调演变。
将舍入模式更改为向上,并添加最小的正浮点数。最小的正浮点数也是两个浮点数之间可以存在的最小增量,因此永远不会跳过任何浮点数。最小的正浮点数为double
。
为了完整起见,我将补充一点,即使不将舍入模式从“最接近”默认值更改,将浮点数乘以FLT_MIN * FLT_EPSILON
也会产生一个数字,该数字可以是远离零的直接邻居,或者那之后的邻居。它可能是最便宜的,如果你已经知道你希望增加/减少浮动的符号,你不介意它有时不产生直接邻居。函数(1.0f + FLT_EPSILON)
和nextafter
的指定方式是x86上的correct implementation必须测试许多特殊值和FPU状态,因此它的功能相当昂贵。
要趋向零,请乘以nextafterf
。
这显然不适用于1.0f - FLT_EPSILON
,通常用于较小的非规范化数字。
乘以0.0f
前进2 ULPS的值恰好低于2的幂,特别是在区间[0.75 * 2 p ... 2 p )。如果你不介意进行乘法和加法,1.0f + FLT_EPSILON
应适用于所有正常数字(但仍然不适用于零,也不适用于所有小的非正规数字)。
答案 1 :(得分:11)
查看“nextafter”函数,它是标准C的一部分(可能是C ++,但我没有检查)。
答案 2 :(得分:0)
我在我的机器上试了一下。所有三种方法:
1.加1和memcopying
2.添加FLT_EPSILON
3.乘以(1.0f + FLT_EPSILON)
似乎给出了同样的答案
#include <float.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main(int argc, char *argv[])
{
if(argc != 3) {
printf("Usage: <binary> <floating_pt_num> <num_iter>\n");
exit(0);
}
float f = atof(argv[1]);
int count = atoi(argv[2]);
assert(count > 0);
int i;
int num;
float num_float;
printf("Original num: %f\n", f);
for(i=1; i<=count; i++) {
memcpy(&num, &f, 4);
num += i;
memcpy(&num_float, &num, 4);
printf("int added = %f \t%02d-eps added = %f \tmult by %2d*(eps+1) = %f\n", num_float, i, f + i*FLT_EPSILON, i, f*(1.0f + i*FLT_EPSILON));
}
return 0;
}