c语言 - 如何找到浮点数最接近的不相等值?



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)。

两种有时有用的非标准方法是:

  1. 访问float/double作为相同大小的无符号整数的表示,并递增或递减该整数。浮点格式经过精心设计,因此对于正浮点和负浮点,表示形式的位(视为整数)会随着所表示的浮点单调变化。

  2. 将舍入模式更改为向上,并添加最小的正浮点数。最小的正浮点数也是两个浮点之间的最小增量,因此这永远不会跳过任何浮点。最小的正浮点数是FLT_MIN * FLT_EPSILON


为了完整起见,我将补充一点,即使不将舍入模式从"到最近"的默认值更改为四舍五入,将浮点值乘以(1.0f + FLT_EPSILON)也会产生一个数字,该数字要么是离零的近邻,要么是其后的近邻。如果你已经知道你想要增加/减少的浮动符号,并且你不介意它有时不会产生近邻,那么它可能是最便宜的。指定函数nextafternextafterf的方式是,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;
}

最新更新