0
\$\begingroup\$

I am reading 4 bytes from EEPROM to make a float value.

The specific eeprom I use(24Lxx Series) has 0xFF as default value.

So, if I read four bytes of 0xFF into float variable, it will have 0xFFFFFFFF

I want to be able to check above condition.

However, statement, if(floatVariable==0xFFFFFFFF){....} doesn't check out to be true, even if the floatVariable does have 0xFFFFFFFF inside(checked via debugger).

Is there any way I can do that?

\$\endgroup\$
  • \$\begingroup\$ Other than using a union or casting a pointer? \$\endgroup\$ – Ignacio Vazquez-Abrams Jul 5 '16 at 2:30
  • \$\begingroup\$ @IgnacioVazquez-Abrams I've considered using union, but making union for every single float variable would be make the code too messy. Thank you tho \$\endgroup\$ – Steve Jul 5 '16 at 2:50
  • \$\begingroup\$ There no need a volatile pointer or union! Just typecast if ((uint32_t)floatVariable == (uint32_t)0xFFFFFFFF). And if it's too messy - create and use macros: #define IS_WORD_EMPTY(A) ((uint32_t)(A) == (uint32_t)0xFFFFFFFF). Withal you must be sure that float type (or any other you will test) really have size 4 bytes on your platform. \$\endgroup\$ – imbearr Jul 5 '16 at 4:38
  • 2
    \$\begingroup\$ @imbearr Unfortunately, the cast of floatvariable to (unit32_t) will not use the bit representation to do the conversion. The compiler will say "Ah! You're converting a float to a unit32_t? I can do that!" - and then go and do the interpreted conversion that I warned about in my answer, and compare that to ~0u which will NOT work. \$\endgroup\$ – John Burger Jul 5 '16 at 13:30
  • \$\begingroup\$ @JohnBurger Yeah! You right! I have test it... Now I will know it. Thanks. \$\endgroup\$ – imbearr Jul 5 '16 at 14:01
5
\$\begingroup\$

In general, when you're looking at the bit representation of data in memory, you only have two options:

  1. Use a pointer to the variable, and "lie" as to what it is pointing to:

    Note @erebos' and @Lundin's comments below: while you'll often see something like the following, it is NOT strictly conformant C. That "lie" could bite you...

    volatile float f;
    volatile uint32_t *p = (volatile uint32_t *)&f; // Take the address of f and cast it.
    ...
    if (*p!=0xFFFFFFFFu) {
        printf("%f", f);
    } // if
    
  2. Use a union structure to change its interpretation depending on what field you acccess:

    typedef union FloatAs32 {
        volatile float f;
        volatile uint32_t i; // "Overlays" other fields in union
    } FloatAs32;
    
    FloatAs32 f;
    ...
    if (f.i!=0xFFFFFFFFu) { // Access 'i' field
        printf("%f", f.f);  // Access 'f' field
    } // if
    

I say "in general", because you may also have the opportunity to rely on the encoding of the float to get what you want. I'll need to check my references, but it may be that you can test for NaN, which is a special representation of floats to indicate (literally) "Not a Number" (such as sqrt(-1);)

Your test if (f==0xFFFFFFFF) tries to compare the interpreted value of the float against that number. You want to compare against the uninterpreted value.

Edit

I checked my sources, and I was right. If the "Exponent" field is all 1s, and the "Mantissa" field is not all 0s (which matches your case in general, but not specifically all Fs), then the float is interpreted as a NaN. Note that the "Sign" bit is used to distinguish between a "Quiet" NaN (can be propagated through calculations) and a "Signalling" NaN (can cause exceptions). Luckily, a "Sign" bit of 1 (as in your case) is a QNaN.

That means that you can simply use the isnan(f) function and it will return true for your special case - and other cases too. Does your device produce a NaN at any time? Perhaps to indicate an error condition? If so, you shouldn't use this suggestion. If not... that's your call.

Edit #2

@dwelch made a good point in the comments about using volatile. The compiler could keep its values in registers rather than memory, so neither example above would have worked. Adding volatile tells the compiler to always read and write the values to memory rather than caching them. I've modified my examples to take this into account.

\$\endgroup\$
  • 1
    \$\begingroup\$ The first method is commonly referred to as a "typecast" \$\endgroup\$ – Daniel Jul 5 '16 at 2:53
  • 1
    \$\begingroup\$ Just pointing it out as a 1 out of a zillion chance. bitfields fail very often and should never be used. but this trick is used often and so far as I have heard has always worked. Waiting for someone to post a failure someday if they find that one in a zillion compiler and/or platform. I dont know if the spec covers optimization, if you come in as float and go out as float and you have an fpu you can use a floating point register and not touch memory, if you go in as unsigned and come out as unsigned you can use a gpr and not touch memory. so maybe a volatile is required here for safety \$\endgroup\$ – old_timer Jul 5 '16 at 3:48
  • 1
    \$\begingroup\$ Option one might actually violate the strict aliasing rule of C. A pointer may only be cast to the original type or to a char/uint8_t pointer. A compiler could remove that code (maybe even if it's volatile) under the assumption that f and p may never point to or use the same address. \$\endgroup\$ – erebos Jul 5 '16 at 11:30
  • 1
    \$\begingroup\$ @Lundin Note that option 2 is also (sadly) undefined behavior. As far as I know, the only way to win is good old memcpy(). \$\endgroup\$ – pipe Jul 6 '16 at 16:50
  • 1
    \$\begingroup\$ Where non-normative foot note 95 explains: "If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type punning’’). This might be a trap representation." C and C++ are indeed different here. \$\endgroup\$ – Lundin Jul 7 '16 at 6:06
3
\$\begingroup\$

super quick test based on my comments to selected answer. Compiler did what we wanted/desired:

void fdummy ( float );
void dummy ( unsigned int );

union
{
    unsigned int ui;
    float f;
} myun;

void fun ( unsigned int a, float b )
{
    myun.f = b;
    fdummy(myun.f);
    myun.ui = a;
    dummy(myun.ui);
    fdummy(myun.f);
}



00000000 <fun>:
   0:   e92d4070    push    {r4, r5, r6, lr}
   4:   e1a05000    mov r5, r0
   8:   e59f401c    ldr r4, [pc, #28]   ; 2c <fun+0x2c>
   c:   ed840a00    vstr    s0, [r4]
  10:   ebfffffe    bl  0 <fdummy>
  14:   e1a00005    mov r0, r5
  18:   e5845000    str r5, [r4]
  1c:   ebfffffe    bl  0 <dummy>
  20:   ed940a00    vldr    s0, [r4]
  24:   e8bd4070    pop {r4, r5, r6, lr}
  28:   eafffffe    b   0 <fdummy>
  2c:   00000000    andeq   r0, r0, r0

Okay this got it to optimize to registers and it still worked as desired. It copies the unsigned to

void fdummy ( float );
void dummy ( unsigned int );
void fun ( unsigned int a, float b )
{
union
{
    unsigned int ui;
    float f;
} myun;

    myun.f = b;
    fdummy(myun.f);
    myun.ui = a;
    dummy(myun.ui);
    fdummy(myun.f);
}

00000000 <fun>:
   0:   e92d4010    push    {r4, lr}
   4:   e1a04000    mov r4, r0
   8:   ebfffffe    bl  0 <fdummy>
   c:   e1a00004    mov r0, r4
  10:   ebfffffe    bl  0 <dummy>
  14:   ee004a10    vmov    s0, r4
  18:   e8bd4010    pop {r4, lr}
  1c:   eafffffe    b   0 <fdummy>

One or two examples does not a proof make, but the compiler writers wanted this to work even if ram is not used and the goesinta and goesouta of the union dont match.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.