【问题标题】:Converting floating point to unsigned int while preserving order在保留顺序的同时将浮点转换为无符号整数
【发布时间】:2018-09-18 08:59:03
【问题描述】:

我找到了很多关于将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。

【问题讨论】:

  • 你想做什么?如果您只想让您的int 具有float 的截断值,则不是这样。方法就是 unsigned int r = f; 否则它只是一个未定义的行为。
  • 浮点数和整数有非常不同的表示。将一个视为另一个会导致未定义的行为;使用指针还是联合都没有关系。你唯一可以做的就是r = (int)f;,虽然如果f大于UINT_MAX,这会失败。
  • 您是否对获取变量的数值或其按位表示感兴趣?你不能两者都做。
  • 旁白:你可能更喜欢断言(f1 &gt;= f2) == (float2ui(f1) &gt;= float2ui(f2))
  • 代码调用未定义的行为。

标签: c floating-point ieee-754


【解决方案1】:

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

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

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

【讨论】:

  • 这与使用 union (float, unsigned int) 有何不同?
  • @PaulFloyd:可能在 C++ 中,但 C 允许通过联合重新解释字节。
  • 详细信息:“对于非负值 (非 NaNs) ... 保持顺序”。 OTOH,人们可能会认为 NaN 缺乏价值。
  • C 含糊地支持 sunion 重新解释,因为措辞有些不明确和不一致,所以我更喜欢 memcpy 方法,它在任何体面的编译器上都同样有效,并且定义明确。
  • @toohonestforthissite:当然是允许的。对于具有分配存储的对象的有效类型有一些微妙之处,但对于具有声明类型的对象,这些都不起作用。类型不会改变,结果值只是将您存储的字节解释为实际类型中的表示的结果。一般来说,这可能是一个陷阱表示,但这将是实现定义的,而不是未定义/不允许的,对于uint32_t,它特别不能是因为没有备用位要填充。
【解决方案2】:

如果f1f2 是浮点数,而f1 &lt;= f2(int)f1(int)f2 是有效转换,那么(int)f1 &lt;= (int)f2

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

您可以简单地将float2ui 替换为(int)arg0,检查float 是否在int 的范围内。

请注意,如果截断的 float 值超出类型的范围。

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

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

【讨论】:

  • @R..原谅我,但“将浮点数转换为整数”通常意味着将浮点数转换为整数?!
  • 鉴于标签(包括ieee-754),很明显 OP 想要访问表示,并假设,是的,它是实现定义的,但 OP 已经指定了实现约束。
  • 我不会说 OP '显然'试图做任何事情。我的解释和@Bathsheba一样
  • 顺便说一句,像这样保持顺序的转换有很好的理由,例如将值集成为排序键的一部分。
  • @chux:我想知道当他们说“正数”时,OP 是否不允许有符号整数零?当“非负”更好时,经常使用“正”。对于非负面情况,您确实是正确的。
【解决方案3】:

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

在将浮点数放入 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));
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-01
    • 2012-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-23
    • 1970-01-01
    相关资源
    最近更新 更多