在保留顺序的同时将浮点数转换为无符号整数
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.
如果f1
和f2
是浮点数,f1 <= f2
,(int)f1
和(int)f2
是有效转换,那么(int)f1 <= (int)f2
.
换句话说,整数类型的截断从不交换订单轮次。
您可以将 float2ui
替换为 (int)arg0
,检查 float
在 int
的范围内。
请注意 float
到 int
和 float
到 unsigned
的行为 未定义 如果截断的 float
值超出类型的范围。
您当前的代码 - 以某种方式将 float
内存解释为 int
内存 - 具有 undefined 行为。即使通过 union
进行类型双关也会为您提供实现定义的结果;请特别注意 sizeof(int)
不一定与 sizeof(float)
.
相同
如果您使用的是 IEEE754 单精度 float
,32 位 2 的补码 int
,没有陷阱表示,转换为正值,一致的字节顺序,以及对由 NaN
和 +-Inf
表示的各种模式的一些允许,然后由类型双关 影响的转换是 顺序保留。
正如所写,您正在使用的代码具有未定义的行为。如果你想半可移植地访问 float
s 的表示(实现定义,定义明确假设 IEEE 754 并且浮点数和整数字节顺序匹配),你应该这样做:
uint32_t float2ui(float f){
uint32_t r;
memcpy(&r, &f, sizeof r);
return r;
}
对于非负值,浮点值和表示之间的这种映射是保序的。如果您认为您看到它无法保持秩序,我们将需要准确了解您认为哪些值是反例。
使用联合从浮点数中提取位应该可行。如果 c 标准真的支持这个,会有一些讨论。但是无论标准怎么说,gcc 似乎都支持它。而且我希望有太多现有代码需要它,以便编译器删除支持。
在将 float 放入 int 并保持顺序时,您必须注意一些事项。
- 像
nan
这样的有趣值没有任何顺序可以保留
floats
存储为量值和符号位,而ints
为二进制补码
(假设一个健全的架构)。所以对于负值,你必须翻转所有
除符号位外的位
- 如果
float
和 int
在您的体系结构上没有相同的字节顺序,您
还必须转换字节顺序
这是我的实现,在 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));
}
}
我找到了很多关于将 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.
如果f1
和f2
是浮点数,f1 <= f2
,(int)f1
和(int)f2
是有效转换,那么(int)f1 <= (int)f2
.
换句话说,整数类型的截断从不交换订单轮次。
您可以将 float2ui
替换为 (int)arg0
,检查 float
在 int
的范围内。
请注意 float
到 int
和 float
到 unsigned
的行为 未定义 如果截断的 float
值超出类型的范围。
您当前的代码 - 以某种方式将 float
内存解释为 int
内存 - 具有 undefined 行为。即使通过 union
进行类型双关也会为您提供实现定义的结果;请特别注意 sizeof(int)
不一定与 sizeof(float)
.
如果您使用的是 IEEE754 单精度 float
,32 位 2 的补码 int
,没有陷阱表示,转换为正值,一致的字节顺序,以及对由 NaN
和 +-Inf
表示的各种模式的一些允许,然后由类型双关 影响的转换是 顺序保留。
正如所写,您正在使用的代码具有未定义的行为。如果你想半可移植地访问 float
s 的表示(实现定义,定义明确假设 IEEE 754 并且浮点数和整数字节顺序匹配),你应该这样做:
uint32_t float2ui(float f){
uint32_t r;
memcpy(&r, &f, sizeof r);
return r;
}
对于非负值,浮点值和表示之间的这种映射是保序的。如果您认为您看到它无法保持秩序,我们将需要准确了解您认为哪些值是反例。
使用联合从浮点数中提取位应该可行。如果 c 标准真的支持这个,会有一些讨论。但是无论标准怎么说,gcc 似乎都支持它。而且我希望有太多现有代码需要它,以便编译器删除支持。
在将 float 放入 int 并保持顺序时,您必须注意一些事项。
- 像
nan
这样的有趣值没有任何顺序可以保留 floats
存储为量值和符号位,而ints
为二进制补码 (假设一个健全的架构)。所以对于负值,你必须翻转所有 除符号位外的位- 如果
float
和int
在您的体系结构上没有相同的字节顺序,您 还必须转换字节顺序
这是我的实现,在 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));
}
}