I was working on something and saw this oddity, thought I would share it.

$ ./a.out
in.val = 86.3115158 -> Its a valid floating point number
out1.val = nan -> Swapped it and now its a NaN! That's OK swap it back.
out2.val = 86.4365158 -> What, its not the same number???
in.m = {0x7f, 0x9f, 0xac, 0x42} -> Notice the second number is 0x9f
out1.m = {0x42, 0xac, 0xdf, 0x7f} -> But in the flipped version it became 0xdf?
out2.m = {0x7f, 0xdf, 0xac, 0x42} -> So, this can't be the same number.

The error comes from when I swaped the first number 86.3115158, its flipped version produced a NaN.
That would be OK if the flipped version just returned the same number, but that is not the case!
I surmised that when I assign the NaN value to another float, the 'a' bit in the register was
changed to indicate a Quiet NaN http://en.wikipedia.org/wiki/NaN#Floating_point

So, I changed the byteSwap function to not return the result but just modify the input.
in.val = 86.3115158 -> Its a valid float.
in.val = nan -> Swapped it and now its a NaN!
in.val = 86.3115158 -> Swap it back and its OK!

Because I don't do an assignment to a float it works.
$

Here is the code
#include <string>
#include <iostream>
#include <iomanip>

//-------------------------------------------------------------------------
// Swap stuff
template <class T> inline void byteSwap2(T& v) {
    union {
      T val;
      char m[sizeof(T)];
    } in, tmp;
    in.val = v;
    for (unsigned int j(1); j <= sizeof(T); j++) {
      tmp.m[j-1] = in.m[sizeof(T)-j];
    }
    memcpy(&v,tmp.m,sizeof(T));
    return;
}

//-------------------------------------------------------------------------
// Swap stuff
template <class T> inline T byteSwap(const T& v) {
   unsigned int j(1);
   union {
      T val;
      char m[sizeof(T)];
   } in, tmp;
   in.val = v;
   //----------------------------------------------------------------------
   // Swap the data
   for (; j <= sizeof(T); j++)
      tmp.m[j-1] = in.m[sizeof(T)-j];
   return (tmp.val);
}
//-------------------------------------------------------------------------
// Print stuff
void printIt(const char *name,char o[],int s){
   unsigned int j(0);
   std::cout << name << "   = {";
   for (; j<s-1; j++)
      std::cout << std::hex << std::showbase << int(o[j]&0xff) << ", ";
   std::cout << int(o[j]&0xff) << "}"; 
}
int main(){
   unsigned int j(0);
   union {
      float val;
      char m[sizeof(float)];
   } in,out1,out2;

   //----------------------------------------------------------------------
   // I need a specific value for the floating point number, so in order
   // to get it exact I load it like this
   in.m[0] = 0x7f, in.m[1] = 0x9f, in.m[2] = 0xac, in.m[3] = 0x42;

   //----------------------------------------------------------------------
   // in.val is a valid floating point number
   std::cout << std::setprecision(9) << "in.val   = " << in.val;
   std::cout << "                -> Its a valid floating point number" << std::endl;
   //----------------------------------------------------------------------
   // Swap the number
   out1.val = byteSwap(in.val);
   //----------------------------------------------------------------------
   // Print out result, its a NaN?
   std::cout << "out1.val = " << out1.val;
   std::cout << "                       -> Swapped it and now its a NaN!  That's OK swap it back." << std::endl;
   //----------------------------------------------------------------------
   // That OK, swap it back
   out2.val = byteSwap(out1.val);
   std::cout << "out2.val = " << out2.val;
   std::cout << "                -> What, its not the same number???" << std::endl;

   printIt("in.m  ", in.m,sizeof(out1.m));
   std::cout << "  -> Notice the second number is 0x9f" << std::endl;
   printIt("out1.m", out1.m,sizeof(out1.m));
   std::cout << "  -> But in the flipped version it became 0xdf?" << std::endl;
   printIt("out2.m", out2.m,sizeof(out1.m));
   std::cout << "  -> So, this can't be the same number." << std::endl;

   std::cout << "\nThe error comes from when I swaped the first number " 
             << in.val << ", its flipped version produced a NaN.\n" 
             << "That would be OK if the flipped version just returned the"
             << " same number, but that is not the case!\n" 
             << "I surmised that when I assign the NaN value to another "
             << "float, the 'a' bit in the register was\n"
             << "changed to indicate a Quiet NaN http://en.wikipedia.org/wiki/NaN#Floating_point\n\n"
             << "\nSo, I changed the byteSwap function to not return the " 
             << "result but just modify the input." << std::endl;

   //----------------------------------------------------------------------
   // Print out valid floating point number
   std::cout << "in.val   = " << in.val;
   std::cout << "                -> Its a valid float." << std::endl;
   //----------------------------------------------------------------------
   // Swap the number
   byteSwap2(in.val);
   //----------------------------------------------------------------------
   // Print out result, its a NaN?
   std::cout << "in.val   = " << in.val;
   std::cout << "                       -> Swapped it and now its a NaN!" << std::endl;
   //----------------------------------------------------------------------
   // Swap the number
   byteSwap2(in.val);
   //----------------------------------------------------------------------
   // Print out result, its correct
   std::cout << "in.val   = " << in.val;
   std::cout << "                -> Swap it back and its OK!" << std::endl;

   std::cout << "\nBecause I don't do an assignment to a float it works." << std::endl;

   return 0; 
}

Recommended Answers

All 2 Replies

With what compiler did you get this? I don't think this is necessarily standard-compliant behavior.

gcc version 4.1.2 20080704

vendor_id       : GenuineIntel
model name      : Intel(R) Xeon(R) CPU   E5645  @ 2.40GHz
cpu MHz         : 2400.265
cache size      : 12288 KB

Would it be a function of the compiler or hardware? I was guessing that when the floating point register gets loaded with the NaN, the register changed that bit to indicate quite NaN.
Thoughts?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.