How to store float value in uint64_t variable in C? - c

I have an array of uint64_t values uint64_t *data; in where I need to store 4 different data types: int,char*, bool, float. I solved every type by simple casting to (uint64_t), but it doesn`t work for float:
float val = 1.5f;
uint64_t var = (uint64_t) val;
printf("%.1f", (float) var); // prints 1.0
Is there a way to move data between variables on even lower level than casts? I've tried to combine casts in every way but the result was 0.0 or 1.0.

... to store float value in uint64_t variable ...
Copy it.
float var_f = 1.5f;
uint64_t var_u64;
_Static_assert(sizeof var_f <= sizeof var_u64, "Wide float");
memcpy(&var_u64, &var_f, sizeof var_f);
To recover
memcpy(&var_f, &var_u64, sizeof var_f);
printf("%g\n", var_f);

Related

Convert the internal representation of a float stored in an integer to an actual float

I have the internal representation of a float stored in a uint32_t. Suppose we have two of them with those conditions. I want to sum the two floats represented by the uint32_t and then store their internal representation inside another uint32_t. I've been trying a few things but I'm not sure if there is an easy or standard way of doing so. From my point of view, there are two problems:
Convert the internal representation stored in the uint32_t to a float (otherwise I wouldn't know how to sum them).
After the sum, store the resulting float internal representation in a uint32_t.
I've been looking at functions in C libraries and maybe it could be done with printf or atof but I have not managed to resolve it.
Well, I finally used memcpy() to solve it. I am not entirely sure that it is completely reliable but I think it works well.
//Generate the floats
float a = 1.0;
float b = 2.1;
uint32_t x;
uint32_t y;
//Copy the internal representation to x, y
memcpy(&x, &a, sizeof(float));
memcpy(&y, &b, sizeof(float));
//This would be the starter point of the problem described above
float c, d;
memcpy(&c, &x, sizeof(float));
memcpy(&d, &y, sizeof(float));
float r = c + d;
printf("The number is %f\n", r);
The number printed is 3.1000 as expected.
I don't understand why you use a typed language to store things in incompatible containers, but let's see if this if of help to you:
union flt_int {
float f;
uint32_t i;
};
...
union flt_int a, b;
a.i = val1; /* val1 is the integer internal rep of a float */
b.i = val2; /* idem. */
c.f = a.f + b.f; /* use the floating point representation to add */
/* now you have in c.i the internal representation of the
* float sum of a and b as a uint32_t */

My floating value doesn't match my value in C

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

C Provide float with binary bits produces the wrong number

So, in C I am trying to give a floating point variable a number in binary bits (or hexadecimal digits) and then print it out, however it doesn't want to print the number I have calculated by hand or with an online converter.
float x = (float) 0b01000001110010000000000000000000;
or
float x = (float) 0x41C80000;
When printed out using
printf("%f", x);
produces results like this:
1103626240.000000
Instead of the expected 25, due to a sign bit of 0, exponent bit of 131, and a fraction of 1.5625.
Why is this, and how can I get the results I want?
The value 0x41C80000 in hex, is an integer that has the value 1103626240 in decimal. In your code, you are casting this value to a float which gives you this result:
x = 1103626240.000000
A solution for this can be made using a union:
union uint_to_float {
unsigned int u;
float f;
};
union uint_to_float u2f;
u2f.u = 0x41C80000;
printf("x = %f\n", u2f.f);
EDIT:
As mentioned by #chux, using uint32_t from stdint.h, instead of unsigned int is a better solution.

Typecasting from int,float,char,double

I was trying out few examples on do's and dont's of typecasting. I could not understand why the following code snippets failed to output the correct result.
/* int to float */
#include<stdio.h>
int main(){
int i = 37;
float f = *(float*)&i;
printf("\n %f \n",f);
return 0;
}
This prints 0.000000
/* float to short */
#include<stdio.h>
int main(){
float f = 7.0;
short s = *(float*)&f;
printf("\n s: %d \n",s);
return 0;
}
This prints 7
/* From double to char */
#include<stdio.h>
int main(){
double d = 3.14;
char ch = *(char*)&d;
printf("\n ch : %c \n",ch);
return 0;
}
This prints garbage
/* From short to double */
#include<stdio.h>
int main(){
short s = 45;
double d = *(double*)&s;
printf("\n d : %f \n",d);
return 0;
}
This prints 0.000000
Why does the cast from float to int give the correct result and all the other conversions give wrong results when type is cast explicitly?
I couldn't clearly understand why this typecasting of (float*) is needed instead of float
int i = 10;
float f = (float) i; // gives the correct op as : 10.000
But,
int i = 10;
float f = *(float*)&i; // gives a 0.0000
What is the difference between the above two type casts?
Why cant we use:
float f = (float**)&i;
float f = *(float*)&i;
In this example:
char ch = *(char*)&d;
You are not casting from double to a char. You are casting from a double* to a char*; that is, you are casting from a double pointer to a char pointer.
C will convert floating point types to integer types when casting the values, but since you are casting pointers to those values instead, there is no conversion done. You get garbage because floating point numbers are stored very differently from fixed point numbers.
Read about the representation of floating point numbers in systems. Its not the way you're expecting it to be. Cast made through (float *) in your first snippet read the most significant first 16 bits. And if your system is little endian, there will be always zeros in most significant bits if the value containing in the int type variable is lesser than 2^16.
If you need to convert int to float, the conversion is straight, because the promotion rules of C.
So, it is enough to write:
int i = 37;
float f = i;
This gives the result f == 37.0.
However, int the cast (float *)(&i), the result is an object of type "pointer to float".
In this case, the address of "pointer to integer" &i is the same as of the the "pointer to float" (float *)(&i). However, the object pointed by this last object is a float whose bits are the same as of the object i, which is an integer.
Now, the main point in this discussion is that the bit-representation of objects in memory is very different for integers and for floats.
A positive integer is represented in explicit form, as its binary mathematical expression dictates.
However, the floating point numbers have other representation, consisting of mantissa and exponent.
So, the bits of an object, when interpreted as an integer, have one meaning, but the same bits, interpreted as a float, have another very different meaning.
The better question is, why does it EVER work. You see, when you do
typedef int T;//replace with whatever
typedef double J;//replace with whatever
T s = 45;
J d = *(J*)(&s);
You are basically telling the compiler (get the T* address of s, reintepret what it points to as J, and then get that value). No casting of the value (changing the bytes) actually happens. Sometimes, by luck, this is the same (low value floats will have an exponential of 0, so the integer interpretation may be the same) but often times, this'll be garbage, or worse, if the sizes are not the same (like casting to double from char) you can read unallocated data (heap corruption (sometimes)!).

Problems with negative numbers

I Have this "simple" code.
union
{
unsigned int res;
char bytes[2];
} ADC;
char ADC_num[5];
float temprature;
void vis_temp (void) // Show temp..
{
signed int l, length;
unsigned int rem;
GO_nDONE=1; // initiate conversion on the channel 0
while (GO_nDONE) continue;
ADC.bytes[0]=ADRESL;
ADC.bytes[1]=ADRESH;
utoa(ADC_num, ADC.res, 10);
temprature = (float) ADC.res * 478.1 / 1024;
temprature = temprature - 50.0;
l = (signed int) temprature;
temprature -= (float) l;
rem = (unsigned int)(temprature* 1e1);
sprintf(&ADC_num, "%i.%u", l, rem);
When reading ADC_res (voltage on pin, temperature sensor) that temperature is 0 degree or under then program writes "0.65500" instead of "-3.5" or similar.
I should have declared the right as signed and unsigned int.
Any hints to fix it, or have an other way of converting it.
temprature = (float) ADC.res * 478.1 / 1024;
temprature = temprature - 50.0;
Suppose now temprature has a negative value -x.yz.
l = (signed int) temprature;
Now l = -x, and
temprature -= (float) l;
temprature = -x.yz - (-x) = -0.yz.
rem = (unsigned int)(temprature* 1e1);
Multiply with 10, and convert to unsigned int. Usually, that results in undefined behaviour (6.3.1.4 (1)):
When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.61)
61) The remaindering operation performed when a value of integer type is converted to unsigned type need not be performed when a value of real floating type is converted to unsigned type. Thus, the range of portable real floating values is (−1, Utype_MAX+1).
But converting the negative value to unsigned int would produce the wrong result anyway, even if the remaindering operation is done, what you want is the absolute value, so you should convert
rem = (unsigned int)fabsf(temprature * 1e1);
there.
I think the problem may be coming from the call to the utoa() function.
The utoa() function prototype is generally as follow
char * utoa(unsigned int n, char * buffer, int radix);
In your code you have inverted the two first parameters. You are modifying the ADC structure through this call. I am curious how this could ever be compiled without any error? Any decent compiler would complain about passing an argument which is not a pointer type.
Try with the following
utoa(ADC.res, ADC_num, 10);

Resources