How to do comparisons of float values using series of bitwise operations?
9 回答
Don't. Just... don't. Use ==
, or its wild and wacky neigbors >
and <
. There's even the crazy hybrids, <=
and >=
! These should cover all of your float-comparison needs.
Update: Never mind, don't use ==
. The others should be fine.
Update update: Not using ==
means you probably shouldn't use <=
or >=
, either. The moral of the story is that floats are tricksy, which is why you absolutely, definitely shouldn't be trying bitwise operations on them.
在某些情况下,按位操作浮点数实际上是有意义的。例如,如果您尝试对具有较低位精度或较大位精度的硬件进行建模。在这些情况下,您将需要访问浮点数上的位。正如 bta 所说,您首先应该知道的是 IEEE 754 标准,以便您知道您正在操作哪些位。
然后您可以使用诸如 ShinTakezou 之类的解决方案,但我会建议一些更复杂的解决方案。
假设我们想要单精度。
首先我们声明一个具有不同浮点字段的结构:
typedef struct s_float{
int sign : 1;
int exponent : 8;
int mantissa : 23;
}my_float_struct;
然后我们这样声明一个联合:
union u_float{
float the_float;
my_float_struct the_structure;
}my_float;
然后我们可以通过执行以下操作访问代码中的浮点数:
my_float.the_float = <float number>;
位字段的使用例如如下:
printf("%f is %d.%d.%d\n",my_float.the_float,my_float.the_structure.sign,my_float.the_structure.exponent,my_float.the_structure.mantissa);
而且您可以分配、修改字段或您需要的任何其他内容。
我不认为在 a 上使用按位运算符float
会做你认为它会做的事情。在此之前,请确保您熟悉IEEE 754标准,该标准控制浮点数在内部的表示方式。虽然这是一个完全有效的操作,但它很可能不是很有用。
你到底想完成什么?可能有更好的方法来做到这一点。
由float
数字表示的域不适合它们的位实现和操作。
您可以轻松地在您的浮点数上应用任何按位运算符,但您不会获得任何有用的信息,因为如果您想将它们视为浮点数,这些运算符将以一种根本没有任何意义的方式修改数字。
对两个指数进行与运算或对两个尾数进行异或运算会有什么意义?我的意思是在实际的浮动操作中..
浮点布局强烈依赖于平台。我记得看到丑陋的黑客只使用整数运算来绘制随机浮点数,但这绝对是不可移植的。
Convert to integral type. Then You can use all the binary operations with it. Beware that conversion is temporary (type conversion is like a function returning value interpreted as if it had different type).
float real_value;
(int)real_value | 3, // this will work
real_value | 3; // This will not work
如果您仔细阅读有关 fp 编号的 IEEE 规范,那么您就处于起步阶段。下一步是在软件中实现硬件已经完成的工作,如果 cpu 支持 fp 否则你必须在软件中从头开始实现 fp 操作(根据 IEEE)......两者都完全可行(这是一个必须要做的)如果需要 IEEE 754,则在将 IEEE 用于 cpus 或 fp 协处理器之前执行此操作)。
让我们假设您的目标根本无法 fp,因此您需要从头开始实现它。然后由您决定如何将数字存储在内存中(您可以同意系统的字节顺序);例如,1.23 的浮点数在我的机器(LE)上作为 0xA4709D3F 存储到 mem 中,实际上“正确”的方式是 0x3F9D70A4(我们的写作方式更像是 BE 而不是 LE,但没有“正确”的方式......即使这种方式允许我们直接使用规格检查数据,所以如果我写 -1.23,我会得到 0xBF9D70A4,很明显符号位被提升到 1)
但是由于我们要从头开始实现它,我们可以通过这种方式将数字写入内存:
unsigned char p[4];
p[0] = 0x3f; p[1] = 0x9d; p[2] = 0x70; p[3] = 0xa4;
然后是困难的部分......就像这样:
bool is_positive(float_t *p)
{
return ! (p[0] & 0x80); // or alike
}
假设我们的 proc 无法处理 32 位(或更多)整数,我们在内存中工作。当然,我选择了更简单的操作......!其他的比较难,但是从 IEEE 754 的描述开始,做一些推理,你就可以实现你想要的。如您所见,这并不容易......当没有浮点单元时,您可以在某个地方找到实现对 fp 数进行操作的库,但现在我找不到任何库(示例可能是 Amiga mathieeedoubbas.library,但我认为你找不到这个的来源,无论如何它可以直接在 m68k asm 中......;只是说软件 impl 可能存在于某处......)
在极少数需要这样做的情况下(例如,处理外来浮点格式,例如在 PC 上处理 VAX G 格式的数据),您通常通过将浮点数据放入整数相同的大小,或者放入一个 char 数组(同样,大小正确)。
不要指望这样的代码接近可移植或干净。在典型情况下,您希望尽可能少地这样做,通常只是读入原始数据,创建可能最接近的本机浮点值,然后使用它。即使/如果您必须处理此类外来数据,通常仍应避免使用外来格式进行比较等操作。
下面的链接中有一个很好的描述,它使用该技巧为浮点数创建高度优化的排序函数。
http://www.stereopsis.com/radix.html
显然,在大多数情况下,使用这种 hack 是不可移植且不可取的。