Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which is the reason for avx floating point bitwise logical operations?

Tags:

c++

avx

simd

avx2

AVX allow for bitwise logical operations such as and/or on floating point data-type __m256 and __m256d.

However, C++ doesn't allow for bitwise operations on floats and doubles, reasonably. If I'm right, there's no guarantee on the internal representation of floats, whether the compiler will use IEEE754 or not, hence a programmer can't be sure about how the bits of a float will look like.

Consider this example:

#include <immintrin.h>
#include <iostream>
#include <limits>
#include <cassert>

int main() {

    float x[8] = {1,2,3,4,5,6,7,8};
    float mask[8] = {-1,0,0,-1,0,-1,0,0};
    float x_masked[8];

    assert(std::numeric_limits<float>::is_iec559);

    __m256 x_ = _mm256_load_ps(x);
    __m256 mask_ = _mm256_load_ps(mask);

    __m256 x_masked_ = _mm256_and_ps(x_,mask_);

    _mm256_store_ps(x_masked,x_masked_);

    for(int i = 0; i < 8; i++)
        std::cout << x_masked[i] << " ";

    return 0;
}

Assuming that IEEE754 is used, as the representation of -1 is 0xffffffff, I would expect the output to be

1,0,0,4,0,6,0,0

while it's instead

1 0 0 1.17549e-38 0 1.17549e-38 0 0

Hence my assumption about the internal representation was probably wrong (or I made some silly mistake).

So the question is: is there a way in which I can use floating point logical and be safe about the fact that the result will make sense?

like image 220
Fabio Avatar asked Jul 24 '14 20:07

Fabio


People also ask

Can bitwise operators be used on floats?

Floating point numbers don't have bits at the level of value-representation, which is why you can't apply bitwise operations to them.

Why bitwise operators are not applied to float or double?

Bitwise operators in C should not be used with variable of type float. It is because they are stored in IEEE format (sign bit, exponent and mantissa). Using shift operators with variables of type float / double causes changes in exponent leading to incorrect result.


2 Answers

If you're using AVX intrinsics, then you know you're using IEEE754 floats, because that's what AVX does.

Some of the bitwise operations on floats that make sense are

  • selecting, as in Jens' answer, though as of SSE4.1 we have blendvps and its relatives to do that in one instruction
  • absolute value (mask away the sign)
  • negate (xor with -0.0f)
  • transfer sign
  • extracting the exponent (rare)

Mostly it's for manipulating the sign, or to selectively zero out whole floats, not so much for mucking about with individual bits of the exponent or significand - you can do it, but it's rarely useful.

like image 108
harold Avatar answered Nov 12 '22 07:11

harold


The reason is that there may be penalities for switching between domains of execution units bypass-delays-when-switching-execution-unit-domains and why-do-some-sse-mov-instructions-specify-that-they-move-floating-point-values. In this case switching from a floating point AVX execution unit to an integer execution AVX unit.

For example let's say you want to compare to floating point AVX registers x and y

z = _mm256_cmp_ps(x, y, 1);

The AVX register z contains boolean integer values (0 or -1) which you can then logical AND using _mm256_and_ps or with _mm256_and_si256 if you wanted. But _mm256_and_ps stays in the same execution unit and _mm256_and_si256 switches units which may cause a bypass delay.

Edit: in regards to bitwise operators on floats in C++ it certainly is possible and is sometimes useful. Here are some simple examples.

union {
    float f;
    int i;
} u;
u.i ^= 0x80000000; // flip sign bit of u.f
u.i &= 0x7FFFFFFF; // set sign bit to zero //take absolute value
like image 6
Z boson Avatar answered Nov 12 '22 06:11

Z boson