简体   繁体   中英

assigning from a pointer value

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.

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!

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 .

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.

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!");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM