如何对浮点数执行按位运算

Roh*_*nga 45 c++ floating-point bitwise-operators genetic-algorithm

我试过这个:

float a = 1.4123;
a = a & (1 << 3);
Run Code Online (Sandbox Code Playgroud)

我得到一个编译错误,说操作数&不能是float类型.

当我做:

float a = 1.4123;
a = (int)a & (1 << 3);
Run Code Online (Sandbox Code Playgroud)

我让程序运行.唯一的事情是按位操作是在舍入后获得的数字的整数表示上完成的.

以下也是不允许的.

float a = 1.4123;
a = (void*)a & (1 << 3);
Run Code Online (Sandbox Code Playgroud)

我不明白为什么int可以投,void*但不是float.

我这样做是为了解决Stack Overflow问题中描述的问题如何使用遗传算法求解线性方程?.

AnT*_*AnT 73

在语言层面,没有"对浮点数进行按位运算"这样的事情.C/C++中的按位运算用于数字的值表示.并且在C/C++中没有定义浮点数的值表示.浮点数在值表示级别没有位,这就是为什么不能对它们应用按位运算的原因.

您所能做的就是分析浮点数占用的原始内存的位内容.为此,您需要使用下面建议的union或(等效地,仅在C++中)将浮点对象重新解释为对象数组unsigned char,如

float f = 5;
unsigned char *c = reinterpret_cast<unsigned char *>(&f);
// inspect memory from c[0] to c[sizeof f - 1]
Run Code Online (Sandbox Code Playgroud)

并且,请不要尝试将float对象重新解释为int对象,正如其他答案所暗示的那样.这没有多大意义,这是非法的,并且不能保证在优化中遵循严格别名规则的编译器中工作.在C++中检查内存内容的唯一合法方法是将其重新解释为数组[signed/unsigned] char.

另请注意,从技术上讲,您不能保证系统上的浮点表示是IEEE754(尽管在实践中它是除非您明确允许它不存在,然后仅针对-0.0,±无穷大和NaN).

  • 投票:) C和C++语言就像数学.正式陈述的正确性由硬性事实和硬性证据定义,而不是由多数人的共识定义.多数(票)无关紧要. (14认同)
  • @Chap:你很困惑.与`int`的差异是*巨大的*.机器字节中`char`的大小取决于系统,但语言级别的`char`大小不是.`char`的大小在语言级别总是1,这意味着每个其他类型的大小可以被`char`的大小整除.另外,`unsigned char`中没有填充位,所有位组合都有效.关于`int`你不能这么说.这就是为什么C++中的每个对象都可以重新解释为`char`s的数组,但不能重新解释为`int`的数组. (5认同)
  • @Chap:你所说的关于`float`的系统相关表示的说法是正确的,但这正是我的回答.正如我所说,你只能检查一个`float`对象的*raw memory*表示,这与它是"系统相关的"同义.关键是如果OP*想要/需要*由于某种原因检查`float`的原始内存表示,那么就是这样做的. (3认同)
  • @Chap:在C中实现定义*和在C中使用*undefined*之间有区别.当我需要做一些实现定义时,我仍然希望:1)将系统依赖性保持在最低限度,2 )如果可能,避免依赖未定义的行为.这使得`unsigned char`数组解决方案比`int`解决方案更好. (3认同)
  • IEEE有一些浮点标准使得浮点数更加均匀.他们仍然不适合随意的按位操作. (2认同)
  • @Asad:这个概念是在 C++ 标准中定义的(例如 C++03 的 3.9/4)。每个对象类型都有*对象表示*和*值表示*。*对象表示*是对象的原始内存布局,包括值形成位和填充位。*值表示*仅适用于值形成位并描述这些位如何编码目标值。 (2认同)
  • @ntg:首先,异或版本不是更快。它实际上要慢一些。(但是这个奇怪的神话由于某种原因而拒绝死亡。)其次,如果一个人真的想那样做,那么可以(并且应该)通过将这些值所占用的内存重新解释为整数来做到这一点。 (2认同)

mob*_*mob 17

如果您尝试更改浮点表示中的位,您可以执行以下操作:

union fp_bit_twiddler {
    float f;
    int i;
} q;
q.f = a;
q.i &= (1 << 3);
a = q.f;
Run Code Online (Sandbox Code Playgroud)

正如AndreyT指出的那样,访问这样的联合会调用未定义的行为,编译器可能会长出武器并扼杀你.做他的建议而不是.

  • 从技术上讲,这是未定义的行为.您只能访问上次写入的联合成员. (9认同)

Cha*_*hap 6

float a = 1.4123;
unsigned int* inta = reinterpret_cast<unsigned int*>(&a);
*inta = *inta & (1 << 3);
Run Code Online (Sandbox Code Playgroud)

  • C++强制转换(XXX_cast <>)是首选,因为1)它们更容易搜索,并且2)reinterpret_cast清楚地表明您正在做一些与系统相关的事情,并且有潜在危险. (4认同)
  • 或者稍微冗长一点:`reinterpret_cast <int&>(a)&=(1 << 3)` (3认同)
  • 为什么不只是 `(int*)(void*)&amp;a` ? (2认同)
  • 取消引用指针“inta”会导致未定义行为(请参阅严格别名)。所以这个方法行不通。 (2认同)

Jus*_*tin 5

看看下面的内容.灵感来自快速反平方根:

#include <iostream>
using namespace std;

int main()
{
    float x, td = 2.0;
    int ti = *(int*) &td;
    cout << "Cast int: " << ti << endl;
    ti = ti>>4;
    x = *(float*) &ti;
    cout << "Recast float: " << x << endl;
    return 0; 
}
Run Code Online (Sandbox Code Playgroud)


Pat*_*rts 5

您可以解决严格别名规则,并对float类型双关语执行按位运算uint32_t(如果您的实现定义了它,大多数情况下是这样的),而没有未定义的行为,方法是使用memcpy()

float a = 1.4123f;
uint32_t b;

std::memcpy(&b, &a, 4);
// perform bitwise operation
b &= 1u << 3;
std::memcpy(&a, &b, 4);
Run Code Online (Sandbox Code Playgroud)

  • @NicholasKinar,直到使用 [`bit_cast`](https://gist.github.com/shafik/848ae25ee209f698763cffee272a58f8#c20-and-bit_cast) 进行类型双关的 C++20 提案标准化之前,我不知道有更好的方法(或同样正确的)C++ 中的解决方案,除了回避问题并使用像接受的答案一样的“unsigned char*”。 (2认同)