float
(也称为单个)值是一个4字节的值,应该表示任何实数。由于它的格式和字节数有限,它可以表示一个最小值和一个最大值,并且它的精度有限,这取决于它自己的值。
我想知道,在给定浮点的有限精度的情况下,是否有一种方法可以在某个参考值之上或之下获得最接近的值。对于整数,这是微不足道的:只需加1或减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++的答案感兴趣,但我认为这个答案适用于大多数编程语言。
查找浮点值邻居的标准方法是函数nextafter
用于double
,函数nextafterf
用于float
。第二个论点给出了方向。请记住,在IEEE 754浮点中,无穷大是合法值,因此您可以很好地调用nextafter(x, +1.0/0.0)
以获得x
之上的值,这甚至对DBL_MAX
也有效(而如果您编写nextafter(x, DBL_MAX)
,则在应用于x == DBL_MAX
时会返回DBL_MAX
)。
两种有时有用的非标准方法是:
-
访问
float
/double
作为相同大小的无符号整数的表示,并递增或递减该整数。浮点格式经过精心设计,因此对于正浮点和负浮点,表示形式的位(视为整数)会随着所表示的浮点单调变化。 -
将舍入模式更改为向上,并添加最小的正浮点数。最小的正浮点数也是两个浮点之间的最小增量,因此这永远不会跳过任何浮点。最小的正浮点数是
FLT_MIN * FLT_EPSILON
。
为了完整起见,我将补充一点,即使不将舍入模式从"到最近"的默认值更改为四舍五入,将浮点值乘以(1.0f + FLT_EPSILON)
也会产生一个数字,该数字要么是离零的近邻,要么是其后的近邻。如果你已经知道你想要增加/减少的浮动符号,并且你不介意它有时不会产生近邻,那么它可能是最便宜的。指定函数nextafter
和nextafterf
的方式是,x86上的正确实现必须测试许多特殊值和FPU状态,因此其成本相当高。
若要归零,请乘以1.0f - FLT_EPSILON
。
显然,这对0.0f
不起作用,通常对较小的非规范化数也不起作用。
乘以CCD_ 27前进2 ULPS的值刚好低于2的幂,特别是区间[0.75*2p…2p)。如果你不介意做乘法和加法,x + (x * (FLT_EPSILON * 0.74))
应该适用于所有正常数(但仍然不适用于零,也不适用于所有小的非正规数)。
看看"nextafter"函数,它是标准C的一部分(可能是C++,但我没有检查)。
我在我的机器上试用了它。以及所有三种方法:
1.使用1和memcopy进行添加
2.添加FLT_EPSILON
3.乘以(1.0f+FLT_EPSILON)
似乎给出了相同的答案
请在此处查看结果
bash-3.2$cc float_test/float_test 1.023456 10
原始编号:1.023456
int added=1.023456 01 eps added=102.3456乘以01*(eps+1)=1.023456
int added=1.023456 02 eps added=102 3456乘以02*(eps+1)=1.023456
int added=1.023456 03 eps added=102.3456乘以03*(eps+1)=1.023456
int added=1.023456 04 eps added=102.3456乘以04*(eps+1)=1.023456
int添加=1.023457 05 eps添加=1.0234.57乘以05*(eps+1)=1.023457
int添加=1.023457 06 eps添加=1.02345 7乘以06*(eps+1)=1.023457
int added=1.023457 07 eps added=102.3457乘以07*(eps+1)=1.023457
int添加=1.023457 08 eps添加=1.0234.57乘以08*(eps+1)=1.023457
int added=1.023457 09 eps added=102.3457乘以09*(eps+1)=1.023457
int添加=1.023457 10 eps添加=1.0234.57乘以10*(eps+1)=1.023457
代码
#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: %fn", 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) = %fn", num_float, i, f + i*FLT_EPSILON, i, f*(1.0f + i*FLT_EPSILON));
}
return 0;
}