55

我试过这个:

float a = 1.4123;
a = a & (1 << 3);

我收到一个编译器错误,提示操作数&不能是浮点类型。

当我做:

float a = 1.4123;
a = (int)a & (1 << 3);

我让程序运行。唯一的一点是,按位运算是对四舍五入后获得的数字的整数表示进行的。

以下也是不允许的。

float a = 1.4123;
a = (void*)a & (1 << 3);

我不明白为什么int可以强制转换为void*但不能float

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

4

10 回答 10

86

在语言级别,没有“浮点数的按位运算”之类的东西。C/C++ 中的按位运算处理数字的值表示。并且浮点数的值表示在 C/C++ 中没有定义(无符号整数在这方面是一个例外,因为它们的移位被定义为就像它们存储在 2 的补码中一样)。浮点数在值表示级别上没有位,这就是为什么您不能对它们应用按位运算的原因。

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

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

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

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

于 2009-11-12T17:25:08.323 回答
19

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

union fp_bit_twiddler {
    float f;
    int i;
} q;
q.f = a;
q.i &= (1 << 3);
a = q.f;

正如 AndreyT 所指出的,访问这样的联合会调用未定义的行为,编译器可能会长臂并勒死你。而是按照他的建议去做。

于 2009-11-12T16:42:33.933 回答
8
float a = 1.4123;
unsigned int* inta = reinterpret_cast<unsigned int*>(&a);
*inta = *inta & (1 << 3);
于 2009-11-12T16:39:44.207 回答
7

您可以解决严格混叠规则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);
于 2019-09-20T16:31:26.857 回答
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; 
}
于 2009-11-13T11:02:21.670 回答
2

@mobrule:

更好的:

#include <stdint.h>
...
union fp_bit_twiddler {
    float f;
    uint32_t u;
} q;

/* mutatis mutandis ... */

对于这些值,int 可能没问题,但通常,您应该使用无符号整数进行位移以避免算术位移的影响。并且 uint32_t 甚至可以在整数不是 32 位的系统上工作。

于 2009-11-12T16:56:13.300 回答
2

FWIW,浮点上的逐位运算有一个真正的用例(我最近才遇到它) - 为仅支持旧版本 GLSL 的 OpenGL 实现编写的着色器(1.2 及更早版本不支持逐位运算符) ),如果将浮点数转换为整数,则会出现精度损失。

可以使用余数(模)和不等式检查对浮点数执行按位运算。例如:

float A = 0.625; //value to check; ie, 160/256
float mask = 0.25; //bit to check; ie, 1/4
bool result = (mod(A, 2.0 * mask) >= mask); //non-zero if bit 0.25 is on in A

以上假设 A 介于 [0..1) 之间,并且掩码中只有一个“位”要检查,但它可以推广到更复杂的情况。

这个想法基于is-it-possible-to-implement-bitwise-operators-using-integer-arithmetic 中的一些信息

如果甚至没有内置的 mod 功能,那么也可以相当容易地实现。例如:

float mod(float num, float den)
{
    return num - den * floor(num / den);
}
于 2017-04-02T01:55:36.603 回答
1

位运算符不应用于浮点数,因为浮点数是特定于硬件的,无论您可能拥有的硬件是否相似。您想在“它在我的机器上运行良好”上冒险哪个项目/工作?相反,对于 C++,您可以通过在浮点数的“对象”包装器上重载流运算符来获得位移运算符的类似“感觉”:

// Simple object wrapper for float type as templates want classes.
class Float
{
float m_f;
public:
    Float( const float & f )
    : m_f( f )
    {
    }

    operator float() const
    {
        return m_f;
    }
};

float operator>>( const Float & left, int right )
{
    float temp = left;
    for( right; right > 0; --right )
    {
        temp /= 2.0f;
    }
    return temp;
}

float operator<<( const Float & left, int right )
{
    float temp = left;
    for( right; right > 0; --right )
    {
        temp *= 2.0f;
    }
    return temp;
}

int main( int argc, char ** argv )
{
    int a1 = 40 >> 2; 
    int a2 = 40 << 2;
    int a3 = 13 >> 2;
    int a4 = 256 >> 2;
    int a5 = 255 >> 2;

    float f1 = Float( 40.0f ) >> 2; 
    float f2 = Float( 40.0f ) << 2;
    float f3 = Float( 13.0f ) >> 2;
    float f4 = Float( 256.0f ) >> 2;
    float f5 = Float( 255.0f ) >> 2;
}

您将有一个剩余部分,您可以根据您想要的实现将其丢弃。

于 2012-02-08T11:53:14.070 回答
1

浮点按位运算的浮点按位运算(Python 配方)中的 Python 实现通过以二进制形式表示从小数点向左和向右无限延伸的数字来工作。因为浮点数在大多数架构上都有一个带符号的零,所以它使用一个补码来表示负数(实际上它只是假装这样做并使用一些技巧来实现外观)。

我确信它可以适应在 C++ 中工作,但必须注意在均衡指数时不要让右移溢出。

于 2011-11-28T23:52:15.380 回答
0

您可以使用指针重新解释字节而不更改实际数据。然后,应用移位(或/和其他按位运算符)并重新解释字节。

#include <stdint.h>

float shiftLeftFloat(float val, int shiftCount) {
    uint32_t valAsInt = *((uint32_t *) &val);
    valAsInt <<= shiftCount;
    val = *((float *) &valAsInt);
    return val;
}          

对于带有 gcc 11.2(和 flag -O3)的 x86-64,编译为:

movd    eax, xmm0 // copy `val` to eax register
mov     ecx, edi  // copy `shiftCount` to ecx register
sal     eax, cl   // shift eax by value of cl (= the lowest 8 bits of ecx)
movd    xmm0, eax // Move result (=eax) to "return" register (=xmm0)
ret

注意:仅使用 的最低 8 位shiftCount。因此,输入值将始终被处理为shiftCount % 32

sal代表shift arithmetic left

于 2022-01-13T19:23:42.503 回答