在保留顺序的同时将浮点数转换为无符号整数

Converting floating point to unsigned int while preserving order

我找到了很多关于将 float 转换为 int 的答案。

我只处理正浮点值。 我一直在使用的一种简单方法是:

unsigned int float2ui(float arg0) {
    float f = arg0;
    unsigned int r = *(unsigned int*)&f;
    return r;
}

以上代码运行良好,但无法保留数字顺序。 按顺序我的意思是:

  float f1 ...;
  float f2 ...;
  assert( ( (f1 >= f2) && (float2ui(f1) >= float2ui(f2)) ) ||
          ( (f1 <  f2) && (float2ui(f1) < vfloat2ui(f2)) ));

我试过使用并集得到相同的结果。 任何的想法? 我使用 Homebrew gcc 5.3.0.

如果f1f2是浮点数,f1 <= f2(int)f1(int)f2是有效转换,那么(int)f1 <= (int)f2 .

换句话说,整数类型的截断从不交换订单轮次。

您可以将 float2ui 替换为 (int)arg0,检查 floatint 的范围内。

请注意 floatint floatunsigned 的行为 未定义 如果截断的 float 值超出类型的范围。

您当前的代码 - 以某种方式将 float 内存解释为 int 内存 - 具有 undefined 行为。即使通过 union 进行类型双关也会为您提供实现定义的结果;请特别注意 sizeof(int) 不一定与 sizeof(float).

相同

如果您使用的是 IEEE754 单精度 float,32 位 2 的补码 int,没有陷阱表示,转换为正值,一致的字节顺序,以及对由 NaN+-Inf 表示的各种模式的一些允许,然后由类型双关 影响的转换是 顺序保留。

正如所写,您正在使用的代码具有未定义的行为。如果你想半可移植地访问 floats 的表示(实现定义,定义明确假设 IEEE 754 并且浮点数和整数字节顺序匹配),你应该这样做:

uint32_t float2ui(float f){
    uint32_t r;
    memcpy(&r, &f, sizeof r);
    return r;
}

对于非负值,浮点值和表示之间的这种映射是保序的。如果您认为您看到它无法保持秩序,我们将需要准确了解您认为哪些值是反例。

使用联合从浮点数中提取位应该可行。如果 c 标准真的支持这个,会有一些讨论。但是无论标准怎么说,gcc 似乎都支持它。而且我希望有太多现有代码需要它,以便编译器删除支持。

在将 float 放入 int 并保持顺序时,您必须注意一些事项。

  1. nan这样的有趣值没有任何顺序可以保留
  2. floats存储为量值和符号位,而ints为二进制补码 (假设一个健全的架构)。所以对于负值,你必须翻转所有 除符号位外的位
  3. 如果 floatint 在您的体系结构上没有相同的字节顺序,您 还必须转换字节顺序

这是我的实现,在 x64

上使用 gcc (Gentoo 6.4.0-r1 p1.3) 6.4.0 进行了测试
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

union ff_t
{
  float f;
  unsigned char a[4];
  int i;
};

int same_endianess = 0;

void
swap_endianess(union ff_t *ff)
{
  if (!same_endianess)
    {
       unsigned char tmp;
       tmp = ff->a[0];
       ff->a[0] = ff->a[3];
       ff->a[3] = tmp;

       tmp = ff->a[1];
       ff->a[1] = ff->a[2];
       ff->a[2] = tmp;
    }
}

void
test_endianess()
{
  union ff_t ff = { ff.f = 1 };

  if (ff.i == 0x3f800000)
    same_endianess = 1;
  else if (ff.i == 0x803f)
    same_endianess = 0;
  else
    {
      fprintf(stderr, "Architecture has some weird endianess");
      exit(1);
    }
}

float
random_float()
{
   float f = random();
   f -= RAND_MAX/2;

   return f;
}

int
f2i(float f)
{
  union ff_t ff = { .f = f };

  swap_endianess(&ff);

  if (ff.i >= 0)
    return ff.i;

  return ff.i ^ 0x3fffffff;
}

float
i2f(int i)
{
  union ff_t ff;
  if (i >= 0)
    ff.i = i;
  else
    ff.i = i ^ 0x3fffffff;

  swap_endianess(&ff);

  return ff.f;
}


int
main()
{
  /* Test if floats and ints uses the same endianess */
  test_endianess();

  for (int n = 0; n < 10000; n++)
    {
       float f1 = random_float();
       int i1 = f2i(f1);
       float f2 = random_float();
       int i2 = f2i(f2);

       printf("\n");
       printf("0x%08x,  %f\n", i1, f1);
       printf("0x%08x,  %f\n", i2, f2);

       assert ( f1 == i2f(i1));
       assert ( f2 == i2f(i2));

       assert ( (f1 <= f2) == (i1 <= i2));
    }
}