Thread: strange behavior when using a floating point

  1. #1
    Registered User
    Join Date
    May 2013
    Posts
    228

    strange behavior when using a floating point

    Hi.
    I'm currently studying about binary floating point representation, and I encountered something quite confusing while writing some stuff to code.

    consider the following case:

    Code:
    void show_bytes(byte_pointer start, int len) {
        for (int i=0; i<len ; i++)
            printf(" %.2x", start[i]);
        printf("\n");
    }
    
    void show_float(float x) {
        show_bytes((byte_pointer) &x, sizeof(float));
    }
    
    int main() {
        float a=84.75;
        show_float(a);
    }
    (I know I shouldn't be using C functions here, but the issue here isn't the programming language in particular...)

    this will print out the following output:
    00 80 a9 42
    which is what I expected (from a little-endian machine).

    but now when I'm trying the other direction:

    Code:
    int main() {
        float a=0x42A98000;
        printf("%f\n", a);
    }
    it'll print the wrong output: 111840608.000000

    I even tried to assign the hex digits in different order, i.e:
    Code:
    int main() {
        float a=0x0080a942;
        printf("%f\n", a);
    }
    but that didn't work.

    what's wrong here?

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    You've not done this very symmetrically -- if you've got to use this byte_pointer thing to look at individual bytes, you've got to use this byte_pointer thing to set individual bytes too.

  3. #3
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    What's wrong here is your expectations of how integral and floating point types are represented. They have different internal representations.

    0x0080a942 is an integral constant. The representation of that value in floating point does not have the 4 bytes 0x00, 0x80, 0xa9, 0x42.

    That is completely normal. A 4-byte float can represent non-integral values, whereas a 4-byte int (or unsigned) variable can only represent integral values. Common sense says that their internal representation is different.

    Your code actually has undefined behaviour, because it is using a type conversion to force the address of a float to be treated as a array of bytes.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  4. #4
    Registered User
    Join Date
    May 2013
    Posts
    228
    Thank you both for commenting.

    So correct me if I'm wrong:
    The constant is treated as an unsigned integer, where 0x0080a942 actually represent:
    0000 0000 | 1000 0000 | 1010 1001 | 0100 0010
    which resulted to: 16,863,876
    and only then, it encode it to floating point number.

    BTW, I should've mentioned that the byte pointer is actually
    Code:
    typedef unsigned char* byte_pointer;

    Quote Originally Posted by grumpy View Post
    Your code actually has undefined behaviour, because it is using a type conversion to force the address of a float to be treated as a array of bytes.
    That was made on purpose, this code was written for learning purposes only, so I'm not concerned about undefined behaviour of this type at the moment.

  5. #5
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by Absurd View Post
    That was made on purpose, this code was written for learning purposes only, so I'm not concerned about undefined behaviour of this type at the moment.
    Maybe so, but you're taking output values from that code which has undefined behaviour, and expecting to get specific results when you use those output values for something else. That is a logical fallacy.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  6. #6
    Registered User
    Join Date
    May 2013
    Posts
    228
    How would you write a simple function to show you the bytes of a variable?

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Absurd
    How would you write a simple function to show you the bytes of a variable?
    Take advantage of the fact that it is well defined to take the address of an object and then cast it to a pointer to char in order to re-interpret the object as a sequence of bytes. Combined in a loop with sizeof, you can then print the numeric value of the bytes of the object.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    Registered User
    Join Date
    May 2013
    Posts
    228
    Quote Originally Posted by laserlight View Post
    Take advantage of the fact that it is well defined to take the address of an object and then cast it to a pointer to char in order to re-interpret the object as a sequence of bytes. Combined in a loop with sizeof, you can then print the numeric value of the bytes of the object.
    Sorry for the ignorance, but how is it different from what I did?

  9. #9
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Absurd
    Sorry for the ignorance, but how is it different from what I did?
    Oh, I see. I think grumpy's claim in post #3 is wrong:
    Quote Originally Posted by grumpy
    Your code actually has undefined behaviour, because it is using a type conversion to force the address of a float to be treated as a array of bytes.
    Admittedly, I made my statement in post #7 based on the assumption that this is C, not C++, because you used C-style I/O and C-style casts, but still think that this holds. The C standard makes this very explicit:
    Quote Originally Posted by C99 Clause 6.3.2.3 Paragraph 7d-e
    When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
    Unfortunately, the C++ standard does not mention the conversion to a pointer to character type in the corresponding section on pointer conversions. On the other hand, it explicitly mentions a way to do what you want, but involving a separate array:
    Quote Originally Posted by C++11 Clause 3.9 Paragraph 2
    For any object (other than a base-class subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes making up the object can be copied into an array of char or unsigned char. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value. [ Example:
    Code:
    #define N sizeof(T)
    char buf[N];
    T obj;                     // obj initialized to its original value
    std::memcpy(buf, &obj, N); // between these two calls to std::memcpy,
                               // obj might be modified
    std::memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type
                               // holds its original value
    —end example ]
    But I don't think this is necessary: a reinterpret_cast<char*>(&obj) should be fine for examining the bytes of obj. The reinterpret_cast will be equivalent to static_cast<char*>(static_cast<void*>(&obj)), so the question is whether the conversion from void* to char* is well defined, and I believe it is since:
    Quote Originally Posted by C++11 Clause 3.9.2 Paragraph 4c
    An object of type cv void* shall have the same representation and alignment requirements as cv char*.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  10. #10
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by laserlight View Post
    Oh, I see. I think grumpy's claim in post #3 is wrong:
    It's a matter of interpretation. Generally speaking, any attempt to treat an object of one type as if it is an unrelated type (i.e. converting a pointer to X to a pointer to Y, where there is no implicit conversion, and then dereferencing as a Y) yields undefined behaviour.

    One could obtain the same effect as the OP without a type conversion using a union
    Code:
    //  assume float and unsigned both have sizeof(4), little endian machine, and same floating point representation as in OP
    //    I have not validated those assumptions
    
    union Thing
    {
        float x;
        char c[4];
        unsigned u;
    };
    
    int main()
    {
        Thing p;
         p.x = 84.75;
    
          //   access  p.c to get the individual characters 00 80 a9 42
    
          //   access p.u and print as hex to get 0080a942
    
          p.u = 0x80a942
    
          //   access p.x to get the value 84.75
    
          //   access p.c to get the individual characters
    
          return 0;
    }
    This achieves the same effect without the explicit type conversion (with some constraints on what types union members can only be). And both C and C++ standards specify that accessing any union member other than the one assigned gives undefined behaviour.

    So what we have is two ways (accessing union members versus type conversion of some sort and dereference) to achieve the same effect. One is specified to have undefined behaviour. The standard specifies some loopholes in which one might potentially consider the other method does not have undefined behaviour (my usage of double-negative is deliberate, as in this context "not undefined" might mean unspecified or implementation-defined - it is a bit hairy to equate "not undefined" with "defined").

    Practically, I treat both methods as equivalent. So, irrespective of loopholes, I treat both as undefined - and avoid doing either - unless I'm willing to have non-portable code.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by grumpy
    Generally speaking, any attempt to treat an object of one type as if it is an unrelated type (i.e. converting a pointer to X to a pointer to Y, where there is no implicit conversion, and then dereferencing as a Y) yields undefined behaviour.
    I agree, however, we are not speaking generally. We are speaking about the particular reinterpretation of an object as a sequence of bytes via a conversion from a pointer of the object type to a pointer to a character type.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Need help, please - very strange behavior
    By snork in forum C Programming
    Replies: 16
    Last Post: 09-26-2011, 01:36 AM
  2. Strange issue in floating point addition
    By stephenwalter in forum C Programming
    Replies: 6
    Last Post: 02-23-2010, 02:28 AM
  3. strange std::map behavior
    By manav in forum C++ Programming
    Replies: 63
    Last Post: 04-11-2008, 08:00 AM
  4. fixed point / floating point
    By confuted in forum Game Programming
    Replies: 4
    Last Post: 08-13-2002, 01:25 PM
  5. Floating point faster than fixed-point
    By VirtualAce in forum A Brief History of Cprogramming.com
    Replies: 5
    Last Post: 11-08-2001, 11:34 PM