A quick introduction to C ++ float ... are there any platform dependencies in this code?

Searching the Internet, I found the following procedure for calculating the float character in IEEE format. This can be easily extended to double as well.

// returns 1.0f for positive floats, -1.0f for negative floats, 0.0f for zero inline float fast_sign(float f) { if (((int&)f & 0x7FFFFFFF)==0) return 0.f; // test exponent & mantissa bits: is input zero? else { float r = 1.0f; (int&)r |= ((int&)f & 0x80000000); // mask sign bit in f, set it in r if necessary return r; } } 

( Source : `` Fast Sign for 32-bit float '', Peter Schoffhauzer)

I am tired of using this procedure, although due to bitwise binary operations. I need my code to work with machines with different byte orders, but I'm not sure how much the IEEE standard indicates, because I could not find the latest version published this year. Can someone tell me if this will work regardless of the byte order of the machine?

Thanks Patrick

+4
source share
2 answers

Do you think fabs() and fabsf() implemented on your system or, when compared, with constant 0? If this is not a bitwise ops, this is entirely possible because the compiler authors do not think it will be faster.

Portability issues with this code:

  • float and int may not have the same precision or even the same size. Therefore, masks may be incorrect.
  • float may not be an IEEE representation
  • You violate strict alias rules. The compiler is allowed to assume that the pointer / link to float and the pointer / link to int cannot point to the same memory location. So, for example, the standard does not guarantee that r initialized to 1.0 before it is changed on the next line. It can reorder operations. This is not idle speculation, and unlike (1) and (2) it is undefined, not for implementation, so you cannot just search for it for your compiler. With sufficient optimization, I saw that GCC skips the initialization of floating point variables that are referenced only by type pointers.

First, I would do the obvious thing and consider the emitted code. Only if it seems dodgy is it worth considering doing something else. I have no particular reason to think that I know more about the bitwise representation of float than my compiler ,-)

 inline float fast_sign(float f) { if (f > 0) return 1; return (f == 0) ? 0 : -1; // or some permutation of the order of the 3 cases } 

[Edit: In fact, GCC does something like this even with -O3. The emitted code is not necessarily slow, but it uses floating point operations, so it is not clear that it is fast. So, the next step will be testing, check if the alternative is faster on any compiler you can rely on, and if it makes it possible for people porting your code to use #define or something else according to the results their own landmark.]

+10
source

Do not forget that to move a floating point value from the FPU register to an integer register, a write to RAM is required followed by reading.

With floating-point code, you'll always look better at a larger image:

 Some floating point code Get sign of floating point value Some more floating point code 

In the above scenario, using the FPU to determine the character will be faster since there will be no overhead for writing / reading 1 . Intel FPU can do:

 FLDZ FCOMP 

which sets condition code flags for > 0 , < 0 and == 0 and can be used with FCMOVcc .

Embedding the above into a well-written FPU code will beat any integer bit manipulations and will not lose accuracy 2 .

Notes:

  • Intel IA32 has a read-after-write optimization where it will not wait for data to be transferred to RAM / cache, but just use the value directly. It still invalidates the cache, although it has a knock effect.
  • The Intel FPU is 80 bits internally, float 32 and doubles 64, so converting to float / double for reloading as an integer will lose some precision bits. These are important bits when you are looking for transitions around 0.
+3
source

Source: https://habr.com/ru/post/1305006/


All Articles