2

I saw the following piece of code in an opensource AAC decoder,

static void flt_round(float32_t *pf)
{
    int32_t flg;
    uint32_t tmp, tmp1, tmp2;

    tmp = *(uint32_t*)pf;
    flg = tmp & (uint32_t)0x00008000;
    tmp &= (uint32_t)0xffff0000;
    tmp1 = tmp;
    /* round 1/2 lsb toward infinity */
    if (flg)
    {
        tmp &= (uint32_t)0xff800000;       /* extract exponent and sign */
        tmp |= (uint32_t)0x00010000;       /* insert 1 lsb */
        tmp2 = tmp;                             /* add 1 lsb and elided one */
        tmp &= (uint32_t)0xff800000;       /* extract exponent and sign */

        *pf = *(float32_t*)&tmp1 + *(float32_t*)&tmp2 - *(float32_t*)&tmp;
    } else {
        *pf = *(float32_t*)&tmp;
    }
}

In that the line,

*pf = *(float32_t*)&tmp;

is same as,

*pf = (float32_t)tmp;

Isn't it?

Or is there a difference? Maybe in performance?

Thank you.

alk
  • 69,737
  • 10
  • 105
  • 255
user2389323
  • 769
  • 2
  • 10
  • 22
  • tmp is not a `float32_t` and I think casting it will change the way it's handled by the program, while casting its address to a pointer to `float32_t` will ensure the program reads the value as-is – Eregrith Jun 10 '15 at 10:32
  • 1
    There is [an easy way](http://ideone.com/Xa0DqJ) to determine whether they are the same. – molbdnilo Jun 10 '15 at 11:05

4 Answers4

1

No, they're completely different. Say the value of tmp is 1. Their code will give *pf the value of whatever floating point number has the same binary representation as the integer 1. Your code would give it the floating point value 1.0!

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • Thanks.. but it gives a "warning: dereferencing type-punned pointer" compiler warning. How can i correct it? – user2389323 Jun 10 '15 at 10:42
  • There are a lot of different ways. One way would be to use a union. – David Schwartz Jun 10 '15 at 10:43
  • float32_t p; memcpy(&p, &tmp, sizeof(float32_t)); is correct, isnt it? where tmp is the variable in the example i have posted. – user2389323 Jun 10 '15 at 10:58
  • They are also different because the form used in the question has a bug which invokes undefined behavior. So "their code" will do anything, it might as well crash and burn as giving the expected value. – Lundin Jun 10 '15 at 11:49
1

This code is editing the value of a float knowing it is formatted using the standard IEEE 754 floating representation.

*(float32_t*)&tmp;

means reinterpret the address of temp as being a pointer on a 32 bit float, extract the value pointed.

(float32_t)tmp;

means cast the integer to float 32. Which means 32.1111f may well produce 32.

UmNyobe
  • 22,539
  • 9
  • 61
  • 90
0

Very different. The first causes the bit pattern of tmp to be reinterpreted as a float. The second causes the numerical value of tmp to be converted to float (within the accuracy that it can be represented including rounding).

Try this:

int main(void) {
    int32_t n=1078530011;

    float32_t f;

    f=*(float32_t*)(&n);
    printf("reinterpet the bit pattern of %d as float - f==%f\n",n,f);

    f=(float32_t)n;
    printf("cast the numerical value of %d as float  - f==%f\n",n,f);

    return 0;
}

Example output:

reinterpet the bit pattern of 1078530011 as float - f==3.141593
cast the numerical value of 1078530011 as float  - f==1078530048.000000

It's like thinking that

const char* str="3568";
int a=*(int*)str;
int b=atoi(str);

Will assign a and b the same values.

Persixty
  • 8,165
  • 2
  • 13
  • 35
0

First to answer the question, my_float = (float)my_int safely converts the integer to a float according to the rules of the standard (6.3.1.4).

When a value of integer type is converted to a real floating type, if the value being converted can be represented exactly in the new type, it is unchanged. If the value being converted is in the range of values that can be represented but cannot be represented exactly, the result is either the nearest higher or nearest lower representable value, chosen in an implementation-defined manner. If the value being converted is outside the range of values that can be represented, the behavior is undefined.

my_float = *(float*)&my_int on the other hand, is a dirty trick, telling the program that the binary contents of the integer should be treated as if they were a float variable, with no concerns at all.

However, the person who wrote the dirty trick was probably not aware of it leading to undefined behavior for another reason: it violates the strict aliasing rule.

To fix this bug, you either have to tell your compiler to behave in a non-standard, non-portable manner (for example gcc -fno-strict-aliasing), which I don't recommend.

Or preferably, you rewrite the code so that it doesn't rely on undefined behavior. Best way is to use unions, for which strict aliasing doesn't apply, in the following manner:

typedef union
{
  uint32_t  as_int;
  float32_t as_float;
} converter_t;

uint32_t value1, value2, value3; // do something with these variables

*pf = (converter_t){value1}.as_float + 
      (converter_t){value2}.as_float - 
      (converter_t){value3}.as_float;

Also it is good practice to add the following sanity check:

static_assert(sizeof(converter_t) == sizeof(uint32_t), 
              "Unexpected padding or wrong type sizes!");
Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396