9

I am using the XC32 compiler from Microchip, which is based on the standard C compiler.

I am reading a 32bit value from a device on a RS485 network and storing this in a unsigned long that I have typedef'ed as DWORD.

i.e.

typedef DWORD unsigned long;

As it stands, when I typecast this value to a float, the value I get is basically the floating point version of it's integer representation and not the proper IEEE-754 interpreted float.

i.e.

DWORD dword_value = readValueOnRS485();
float temp = (float)dword_value;

Here, dword_value would come through in HEX format as 0x4366C0C4 which as a decimal would be represented as 1130807492 so the typecasting of this to a float simply gives me 1.130807492*10^9 or 1130807492.0 which is not what I want.

I want the single precision IEEE-754 representation which would give me a float value of 230.75299072265625

So clearly typecasting to float doesn't work for me. I need a method that can convert this form me. I have looked all over in the XC32 library and I cannot find anything.

Does anyone know of a predefined method that does this interpretation properly for me? Or is there maybe some suggested method I can write? I am trying to avoid writing my own code for this specific task as I am worried I do not find an efficient solution if C already has a function for this.

The interesting thing is, if I do this to a char*, the value is represented on that char* correctly as 230.75:

sprintf(random_char_pointer, "%.2f, dword_value);

Here printing random_char_pointer to screen gives me 230.75 so the sprintf must be handling the interpreting for me correctly. Therefore I am assuming there is something in C already for me. Can anyone assist?

Dino Alves
  • 113
  • 10
  • 2
    `f = *(float*) &uvalue;` (plus/minus alignment considerations) – joop Nov 10 '15 at 15:16
  • @joop Isn't this equivalent to a cast to `float`? – cadaniluk Nov 10 '15 at 15:20
  • 1
    No, I cast a unsigned int *pointer* to a float *pointer* and dereference it. – joop Nov 10 '15 at 15:21
  • There is no standard C compiler. What do you mean with “is based on the standard C compiler?” – fuz Nov 10 '15 at 15:22
  • @joop That's undefined behaviour and *will* lead to problems. Use a union for this purpose. – fuz Nov 10 '15 at 15:22
  • @FUZxxl my apologies. I meant it in a sense that the XC32 compiler has access to the same default libraries like math.h available to use with gcc for example – Dino Alves Nov 10 '15 at 15:24
  • @FUZxxl how would one use a union to achieve this? – Dino Alves Nov 10 '15 at 15:24
  • @DinoAlves See my answer. – fuz Nov 10 '15 at 15:24
  • The reason why it seems to work with `printf` is that you are doing something that is undefined behaviour. It may look like it works but it breaks apart quickly. – fuz Nov 10 '15 at 15:25
  • 2
    BTW: this might be an XY problem. *Why* are you receiving 4-byte chunks into uint32_t typed values? Since the basic unit of tranfer of rs485 is a 8bit character, you could jus as well receive 4 consecutive bytes into a `char data[sizeof floatval];` object, and tranform using `memcpy(&floatval, data, sizeof data);` – joop Nov 10 '15 at 16:01

1 Answers1

8

The recommended way to do stuff like this is to use a union:

union {
    DWORD w;
    float f;
} wordfloat;

wordfloat.w = dword_value;
temp = wordfloat.f;

This does what you want as per ISO 9899:2011 §6.5.2.3 ¶3 footnote 95:

A postfix expression followed by the . operator and an identifier designates a member of a structure or union object. The value is that of the named member,95) and is an lvalue if the first expression is an lvalue. If the first expression has qualified type, the result has the so-qualified version of the type of the designated member.

95) 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.

fuz
  • 88,405
  • 25
  • 200
  • 352
  • Thank you FUZxxl will try this and let you know – Dino Alves Nov 10 '15 at 15:25
  • I'm curious, where does the `volatile` recommendation come from? Ability to do this kind of data reinterpretation is one of the main reason why (ordinary, non-volatile) `union` exists in C. – user4815162342 Nov 10 '15 at 15:26
  • 1
    No, the volatile is not needed (and basically nonsensical, because the two members share the same location, caching their value(s) would not change the outcome) IIRC the union does not guarantee that the two members actual share exactly the same locations (alignment or size of the two members could still differ, just as in my shorthand comment) – joop Nov 10 '15 at 15:27
  • @joop The union guarantees that both members share the same memory as the address of every union member compares equal to the address of the union, thus each member is located at the same address. I though the volatile was needed to circumvent the strict aliasing rule, but apparently it isn't. – fuz Nov 10 '15 at 15:31
  • Nice answer, quote from the standard appreciated. – user4815162342 Nov 10 '15 at 15:48
  • Note: the quote of the standard is *barely* relevant. "the appropiate part" seems to be the appropiate part... – wildplasser Nov 10 '15 at 23:22
  • @wildplasser The standard quote allows us to write to one part of the union and then read the other. Furthermore, all union members are located at the beginning of the memory occupied by the union and as `long` and `float` seem to occupy the same amount of memory on OP's system, this works. – fuz Nov 11 '15 at 09:08