4

I'm trying to interface a board with a raspberry. I have to read/write value to the board via modbus, but I can't write floating point value like the board.

I'm using C, and Eclipse debug perspective to see the variable's value directly. The board send me 0x46C35000 which should value 25'000 Dec but eclipse shows me 1.18720512e+009...

When I try on this website http://www.binaryconvert.com/convert_float.html?hexadecimal=46C35000 I obtain 25,000.

What's the problem?

For testing purposes I'm using this:

int main(){
    
    while(1){ // To view easily the value in the debug perspective 
        float test = 0x46C35000;
        printf("%f\n",test);
    }
    return 0;
}

Thanks!

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
  • 4
    `0x46C35000` is 1187205120, not 25'000. Why do you expect 25'000? – chux - Reinstate Monica Jun 20 '19 at 14:01
  • Because for the board it is 25'000... It's a constructor value for a gauge… If can't view variable as the board i wonder how i will be able to send it the right new value. – Wireless Learning Jun 20 '19 at 14:03
  • 1
    Regarding your `while loop` and your comment next to it: Don’t do this. Instead, configure your IDE/editor properly so that the program output is still shown after the program terminates in debug mode. – Konrad Rudolph Jun 20 '19 at 14:17

4 Answers4

6

When you do this:

float test = 0x46C35000;

You're setting the value to 0x46C35000 (decimal 1187205120), not the representation.

You can do what you want as follows:

union {
    uint32_t i;
    float f;
} u = { 0x46C35000 };

printf("f=%f\n", u.f);

This safely allows an unsigned 32-bit value to be interpreted as a float.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • So this "structure" allow me to use a Hex value, but showing as a float in printf ? It takes twice the amount of memory or is there a magical trick I don't see here ? – Wireless Learning Jun 20 '19 at 14:27
  • 2
    @Arnaud Please read about how a `union` works in C: https://www.tutorialspoint.com/cprogramming/c_unions.htm – Govind Parmar Jun 20 '19 at 14:29
  • 1
    @Arnaud Each member of a `union` starts at the same memory address, so the size of the `union` is the size of the largest member. It allows you to reinterpret a variable of one type as another. – dbush Jun 20 '19 at 14:34
  • 1
    @Arnaud: There are not hexadecimal values or decimal values. A value is a number, and abstract mathematical entity. There are hexadecimal or decimal numerals, in which we interpret the digits in a base to figure out which number they represent. Although C has some association between numerals and types (whether an integer is in hexadecimal may influence whether it is unsigned, and having a period marks it as floating-point), assigning a value to a `float` just sets its value to that value; it is not an instruction to set the bits of the `float` to the apparent bits of a hexadecimal numeral. – Eric Postpischil Jun 20 '19 at 16:58
  • 1
    @dbush: The size of a union is at least as large as its largest member. It may be larger and must be if a smaller member’s alignment requirement forces padding. – Eric Postpischil Jun 20 '19 at 16:59
  • I would like to put multiple union in a struct, how should i do it ? They should be different from each other. I've tried with the union but it's like my différents union are just the same… – Wireless Learning Jun 25 '19 at 12:57
  • @Arnaud you should post that as a new question. Be sure to include a [mcve]. – dbush Jun 25 '19 at 13:00
4

You’re confusing logical value and internal representation. Your assignments sets the value, which is thereafter 0x46C35000, i.e. 1187205120.

To set the internal representation of the floating point number you need to make a few assumptions about how floating point numbers are represented in memory. The assumptions on the website you’re using (IEEE 754, 32 bit) are fair on a general purpose computer though.

To change the internal representation, use memcpy to copy the raw bytes into the float:

// Ensure our assumptions are correct:

#if !defined(__STDC_IEC_559__) && !defined(__GCC_IEC_559)
#    error Floating points might not be in IEEE 754/IEC 559 format!
#endif

_Static_assert(sizeof(float) == sizeof(uint32_t), "Floats are not 32 bit numbers");
float f;
uint32_t rep = 0x46C35000;
memcpy(&f, &rep, sizeof f);
printf("%f\n", f);

Output: 25000.000000.

(This requires the header stdint.h for uint32_t, and string.h for memcpy.)

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • I didn't knew that, I thought the type "float" fixed the representation, and therefore it should be the same in the printf… So you're telling me i have to do additional steps to have it under a float form ? – Wireless Learning Jun 20 '19 at 14:20
  • 3
    @Arnaud `printf` prints the *value*, not the *internal representation* of the variable. – Konrad Rudolph Jun 20 '19 at 14:23
  • 1
    A more pedantic assertion would be `sizeof(float)*CHAR_BIT == 32` or perhaps a cleaner direct test would be `sizeof(float) == sizeof(uint32_t)` – chux - Reinstate Monica Jun 20 '19 at 15:14
  • @chux I knew that somebody would make this observation but I fear that considering the case of `CHAR_BIT` ≠ 8 would confuse OP even more. That said, your second test is indeed cleaner. – Konrad Rudolph Jun 20 '19 at 15:17
  • It may be worth noting that (language lawyer issues apart) the compilers are allowed to optimize away the call to `memcpy`: https://godbolt.org/z/yXS-cN – Bob__ Jun 20 '19 at 15:44
  • @Bob__ Oh it better optimise it away, I’d expect that from any competent compiler. What I honestly find more interesting in the link you posted is that both clang and GCC do tail-call elimination for `printf`. I thought varargs would inhibit this. – Konrad Rudolph Jun 20 '19 at 15:52
3

The constant 0x46C35000 being assigned to a float will implicitly convert the int value 1187205120 into a float, rather than directly overlay the bits into the IEEE-754 floating point format.

I normally use a union for this sort of thing:

#include <stdio.h>

typedef union
{
    float f;
    uint32_t i;
} FU;

int main()
{
    FU foo;

    foo.f = 25000.0;
    printf("%.8X\n", foo.i);

    foo.i = 0x46C35000;
    printf("%f\n", foo.f);

    return 0;
}

Output:

46C35000
25000.000000
Govind Parmar
  • 20,656
  • 7
  • 53
  • 85
0

You can understand how data are represented in memory when you access them through their address:

#include <stdio.h>

int main()
{
    float f25000; // totally unused, has exactly same size as `int'
    int i = 0x46C35000; // put binary value of 0x46C35000 into `int' (4 bytes representation of integer)
    float *faddr; // pointer (address) to float
    faddr = (float*)&i; // put address of `i' into `faddr' so `faddr' points to `i' in memory

    printf("f=%f\n", *faddr); // print value pointed bu `faddr'

    return 0;
}

and the result:

$ gcc -of25000 f25000.c; ./f25000
f=25000.000000

What it does is: put 0x46C35000 into int i

copy address of i into faddr, which is also address that points data in memory, in this case of float type

print value pointed by faddr; treat it as float type

you get your 25000.0.

tansy
  • 486
  • 3
  • 8