Built-in assembly; Bit operation on a float; What's going on here?

This simple piece of code is my problem:

Advanced asm (gcc); Intel Syntax (-masm = intel); Platform - x86

What it should do: return a float with a length of one and a sign (+ -) equal to x.

float signf(float x) { float r = 1; asm volatile ( "and %1,0x80000000;" "or %0,%1;" :"=r"(r):"r"(x)); return r; } 

A call with an arbitrary random number selected using an honest dice roll gives:

  signf of -1352353.3253: -5.60519e-045 
+2
source share
5 answers

The actual problem with your built-in asm is that you only declare r as output, so the compiler optimizes initialization. You should use the constraint "+r" instead of "=r" , and it should work.

The best optimized version might look like this:

 float signf(float x) { float r; __asm__ __volatile__ ( "and %0, 0x80000000;" "or %0, 0x3f800000;" :"=r"(r):"0"(x)); return r; } 

Please note that this function includes the conversion float-> int-> float (via memory), which may affect performance.

Version C of the above code:

 float signf(float x) { union { float f; int i; } tmp, res; tmp.f = x; res.f = 1; res.i |= tmp.i & 0x80000000; return res.f; } 

This generates identical code for me (using gcc 4.4.5).

Simple approach C return x < 0 ? -1 : 1; return x < 0 ? -1 : 1; Generates full FPU code without conversion or memory access (other than loading the operand), so it may work better. It also uses fcmov if available to avoid forking. Need some benchmarking.

+5
source

In C ++ 11, there are two C ++ functions:

 bool std::signbit (x); 

http://en.cppreference.com/w/cpp/numeric/math/signbit

or,

 float f = std::copysign (1.0f, x); 

http://en.cppreference.com/w/cpp/numeric/math/copysign

+4
source

This seems to work well (AT & T syntax):

 float signf(float x) { float r = 1; asm ("andl $0x80000000, %1\n" "\torl %1, %0\n" :"+r"(r):"r"(x)); return r; } 

TBH, I would use copysignf() , as suggested by others. What you are trying to do is not portable, since it is only related to the IA-32 platform and C ++ compilers that can execute this asm() operator.

EDIT 1

BTW, the next version works the same way (and generates pretty much the same instructions as the previous asm() operator), and has no problems with carriers and alias types (unlike union or reinterpret_cast<> based on versions suggested by others) . A.

 float signf3(float x) { unsigned u; std::memcpy(&u, &x, sizeof (u)) ; float r = 1.f; unsigned uone; std::memcpy(&uone, &r, sizeof (uone)); uone |= u & 0x80000000; std::memcpy(&r, &uone, sizeof (r)); return r; } 
+1
source

This question is marked as C ++, so I offer two C ++ sentences that you can allow to optimize your compiler:

  • return x < 0.0f ? -1.0f : 1.0f;
  • return x / std::abs(x); // I believe self-division shouldn't cause 'almost 1.0' numbers to be genereated
0
source

You do not need to use asm for this. The following does what you tried to do (even the correct result for -0.0f).

 float signf(float x) { bool sign=(0!=(*(reinterpret_cast<uint32_t *>(&x)) & 0x80000000)); return sign? -1.0f : 1.0f; } 
0
source

All Articles