floating point constant in C, bit representation - c

It is possible to access to the bit representation of a floating point constant in C;
For example i'd like to assign
uint64_t x = //bit representation of 5.74;
which is represented by
0x40b7ae14
Do you think it is possible?

One way of achieving this is to use a union:
union {
double fltValue;
uint64_t uintValue;
} conversion;
conversion.fltValue = 5.74;
printf("%#llx\n", conversion.uintValue);
Updated with %#x thanks to Aracthor for mentioning it. And %#llx thanks to EOF.
For a working example (with float instead of double) see:
https://ideone.com/p4rH5l

It is possible, but is not portable because you cannot be sure of how a floating point value is represented : C standard does not define it.
You could simply use casting of pointers :
float x = 5.74;
void *pt = &x
uint64_t *ip = pt;
uint64_t i = *ip;
This is formal undefined behaviour because you are casting a pointer to a different type, and you should not do it because you add endian problems to the floating point representation.
The correct way would be :
float x = 5.74;
void *pt = &x
unsigned char *ip = pt;
ip now point to a unsigned char[] of size sizeof(float) containing the binary representation of a float. And no undefined behaviour was invoked because casting a pointer to a void *or a char * is always allowed.

#include <stdint.h>
#include <stdio.h>
int main(void) {
double x = 5.74;
uint64_t y = *((uint64_t*)&x);
printf("0x%016llx", y); /* 0x4016f5c28f5c28f6 */
}

If you are asking for the binary representation of float, then take an integer pointer..assign the address of the float variable to the integer pointer and by derefenencing the value of int pointer, get its binary form..and in the middle if you somehow want to avoid the warning generated by unusual typecasting then you can always use void*

Related

C programming - unexpected output

I am not able to understand how the following code is working:
#include<stdio.h>
int main() {
int i = 100;
int *a = &i;
float *f = (float *)a;
(*f)++;
printf("%d", *a); //getting some garbage value
}
'f' is pointing to the same memory location as that of 'a'. So, (*f)++ should in turn increment the value of i to 101. Where am I going wrong?
Floats and ints are stored with different binary representations. When you cast a float to an int or vice versa, the compiler takes care of this for you. But in your case, you're casting an int* to a float*, so you're modifying a float that has the wrong value, since the binary representation didn't get converted.

Casting a float for int and int to float

Having a little difficulty with pointers. I have to store a float in an array of unsigned ints and be able to pull it out.
I know there is a special way to cast this so I don't reorder the bits, I think this is the correct way to store it when I want to put it into the array:
float f = 5.0;
int newF = (int *) f;
arrayOfInts[0] = *newF
Which seems to successfully store the value in the array.
However, at some point I have to pull the value back out of the array of ints, this is where my confusion comes in (assuming I inputed into the array correctly)
float * f = (float *) arrayOfInts[0]
int result = *f;
however, that gives me the warning: 'cast to pointer from integer of different size'
I can't really think of how to solve that without some sort of long cast.. which doesn't seem right..
I don't want to lose the value or damage the bits.. obviously It will lose decimal point precision.. but I know theirs some way to safety convert back and forth
I have to store a float in an array of unsigned ints and be able to pull it out.
Use a union and unsigned char[]. unsigned char is specified to not have any padding and all bit combinations are valid. This is not always true of many other number types. By overlaying the float with unsigned char[], code can examine each "byte" of the float, one at a time.
union {
float f;
unsigned char uc[sizeof (float)];
} x;
// example usage
x.f = 1.234f;
for (unsigned i = 0; i<sizeof x.uc; i++) {
printf("%u:%u\n", i, 1u*x.uc[i]);
}
Sample output: Yours may vary
0:182
1:243
2:157
3:63
float --> unsigned char[] --> float is always safe.
unsigned char[] --> float --> unsigned char[] is not always safe as a combination of unsigned char[] may not have a valid float value.
Avoid pointer tricks and casting. There are alignment and size issues.
// Poor code
float f = 5.0f;
int newF = *((int *) &f); // Conversion of `float*` to `int*` is not well specified.
Code can also overlay with fixed-width no-padding types like (u)int32_t if they exist (they usually do) and match in size.
#include <stdint.h>
union {
float f;
uint32_t u32;
} x32;
#include <assert.h>
#include <inttypes.h>
// example usage
assert(sizeof x32.f == sizeof x32.u32);
x32.f = 1.234f;
printf("%" PRNu32 "\n", x32.u32);
}
Example output: yours may vary
1067316150
To convert a float to an int
float fval = 123.4f;
int ival = *(int*)&fval;
To convert back
int ival = /* from float */
float fval = *(float*) &ival;
it won't work if float and int are different sizes, but presumably you know that already. The unsigned char union method outlined in other answer for chux is more robust, but over-complicated for what you probably want to do.

C, Creating a 32 bit pointer to a 8 bit array

I have a buffer where each entry in the buffer is 8 bits in size:
uint8_t Buffer[10] = {0x12,0x34,0x56,0x78,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6};
What I need to do is to create pointers to that array, for example 16 bit and 32 bit pointers. For example:
uint32_t *x;
x = Buffer;
uint32_t *y;
y = Buffer+4;
uint16_t *z;
z = Buffer+8;
Where each variable would then read from the array, for example:
x = 0x78563412
y = 0xf4f3f2f1
z = 0xf6f5
This works completely fine, the problem is that I'm getting warnings about incompatible pointer types. So I was wondering if there is an alternative way of doing this or if I'll just have to live with the warnings? Or am I simply doing this completely the wrong way?
Please note that this code will be executed on a single type of platform where the endianness is always the same and the size of data types is always the same.
You should heed the warnings; what you're doing is undefined behavior. Type aliasing like that is undefined behavior, particularly since there is no guarantee that Buffer has the correct alignment such that it can be accessed as an int/short. If Buffer has the correct alignment, then you can just explicitly cast and it's okay (and the warning will go away).
You have two options:
One, you align the buffer as the larger of the two types. Be careful that your pointer arithmetic doesn't throw off the alignment:
#include <stdalign.h>
alignas(int) unsigned char Buffer[10] = {0x12,0x34,0x56,0x78,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6};
unsigned int *x;
x = (unsigned int*)(Buffer);
unsigned int *y;
y = (unsigned int*)(Buffer+4);
unsigned short *z;
z = (unsigned short*)(Buffer+8);
Two, you create an unsigned int/unsigned short variable and then memcpy the bytes you're interested in into the variable:
unsigned int x;
memcpy(&x, Buffer, 4);
unsigned int y;
memcpy(&y, Buffer + 4, 4);
unsigned short z;
memcpy(&z, Buffer + 8, 2);
The problem with your approach is that it assumes a particular endianness of underlying hardware. Different computers will interpret a sequence of hex bytes
01 23 45 67
as eiter
01234567 or 67452301
Your program may compile and run on both systems, but since the result is hardware-specific, the compiler must warn you of the possibility.
The proper way of forcing a particular endianness is by using an array of integers, convert them using hton and ntoh functions, and set individual bytes either directly by casting a pointer to unsigned char*, or with memcpy.
You might want to use a union
#include <stdint.h>
#include <stdio.h>
typedef union{
uint8_t Buffer[10];
struct{
uint32_t x;
uint32_t y;
uint16_t z;
};
}MYSTRUCT;
int main(){
MYSTRUCT b = {0x12,0x34,0x56,0x78,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6};
printf("x=%#x y=%#x z=%#x\n",b.x,b.y,b.z);
return 0;
}

Pointer subtraction confusion

When subtracting two pointers from each other the difference represents the number of elements between them (when pointing to int).
Why is the difference zero when pointing to type double?
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
double data[10] = {1,2,3,4,5,6,7,8,9};
double *iptr1;
double *iptr2;
double val;
iptr1 = &data[0];
iptr2 = &data[9];
val = iptr2 - iptr1;
printf("Distance between the two addresses = %d\n", val);
return 0;
}
You are printing a double value with %d, which expects an integer. Change the type of val to int and everything will work as expected.
Note that, as suggested in the comments below, C defines a type for pointer differences, ptrdiff_t. This is guaranteed to always hold a value arising from a pointer difference, regardless of how far the actual pointers are. Although, if you are absolutely sure that the difference will fit an int, as in your question, that type should be fine as well. But double makes no sense at all, pointer differences cannot be fractional values.
Double is not the correct type for pointer differences. Use ptrdiff_t, as defined in stddef.h instead.
See here for the Data Types
Instead of
printf("Distance between the two addresses = %d\n", val);
use then also
ptrdiff_t val = iptr2 - iptr1;
printf("Distance between the two addresses = %td\n", val);

Different pointer types for the same address

I am relative new to programming so I apologize if my question is basic.
Situation:
I have several float values and an array of pointers to each value. Ex:
float nr1=1.15;
float nr2=2.30;
float nr3=23.34;
....
float * my_address_array[3];
my_address_array[0] = &nr1;
my_address_array[1] = &nr2;
my_address_array[2] = &nr3;
To access one element, I can use:
float temp_value;
float ** ptr_value;
...
ptr_value = &my_address_array[0];
temp_value = **( ptr_value+0); // copy nr1 to temp
temp_value = **( ptr_value+1); // copy nr2 to temp
temp_value = **( ptr_value+2); // copy nr3 to temp
So far so good. On my system float occupies 32 bits (8051 microcontroller). I need to take one float number and separate it into four 8 bit variables. Example for nr2:
My attempt was:
unsigned char storage1;
unsigned char storage2;
unsigned char storage3;
unsigned char storage4;
...
storage1 =(unsigned char) ((**( ptr_value+1)) >> 24) ;
storage2 =(unsigned char) ((**( ptr_value+1)) >> 16) ;
storage3 =(unsigned char) ((**( ptr_value+1)) >> 8) ;
storage4 =(unsigned char) ((**( ptr_value+1)) & 0xff) ;
I get bad operand type. It seems that I cannot use bit shift operations with float numbers (at least google sais that). I can add a new pointer, as in:
char ** ptr_char_value;
...
ptr_char_value = &my_address_array[0]; // generates warning
storage1 = (*(*( ptr_char_value+1)+0));
storage2 = (*(*( ptr_char_value+1)+1));
storage3 = (*(*( ptr_char_value+1)+2));
storage4 = (*(*( ptr_char_value+1)+3));
I do get a warning (which is fair) that I am using a char type pointer for a float value. I am also not sure how reliable this is. Can anyone advise a better solution?
Thank you!
Edit: The code is for a 8051 microcontroller. I would like to make it as fast/optimal as possible.
with this, you are getting pointer to pointer to value
ptr_char_value = &my_address_array[0];
I don't understand how this should work.
It would be better to use pointer to float directly. Cast it to char array and get separate bytes.
char * ptr_char_value = (char *)(my_address_array[0]);
storage1 = ptr_char_value[0];
storage2 = ptr_char_value[1];
storage3 = ptr_char_value[2];
storage4 = ptr_char_value[3];
EDIT:
It is also question if you realy need array of pointers to floats. It is possible to have struct, cast it to array of bytes and use these bytes directly for something.
struct my_data_type {
float nr1;
float nr2;
float nr3;
}
struct my_data_type my_data;
my_data.nr1 = 1.15;
my_data.nr2 = 4.75;
my_data.nr3 = 8.95;
char * ptr_char_value = (char *)&my_data;
// nr1
storage1 = ptr_char_value[0];
...
// nr2
storage5 = ptr_char_value[4];
...
// nr3
storage9 = ptr_char_value[8];
...
try this. You should type cast before you can shift the data. Include stdint.h
storage1 = (uint8_t) ((((uint32_t)**( ptr_value+1))) >> 24) & 0xFF );
Let's begin with the title:
Different pointer types for the same address
That's code smell. In C, aliasing objects through an incompatible pointer type results in undefined behavior, unless the incompatible aliasing type is (qualified or unqualified, signed or unsigned) character type.
More precisely, an object of type cv-qualified or unqualified T may only be accessed through a pointer to qualified or unqualified T. Additionally, if T is an integral type, then accessing via its (qualified or unqualified) signed or unsigned counterpart is permitted as well.
This is colloquially called the "strict aliasing rule".
Hence, you can't do
float f;
*(int *)&f
even if sizeof(float) == sizeof(int).
The possible solutions:
Use memcpy(), like this (preferred):
float f = 3.14;
int i;
memcpy(&i, &f, sizeof f);
Use a union (C99 only – this is UB too in C89…):
union {
float f;
int i;
} pun = { .f = 3.14 };
printf("%d\n", pun.i);
Why do you want to separate float in 4 variables?
That don't make any sense because of how float is represented in computer.
First bit S is sing, 0 for positive 1 for negative numbers. Then 8 bits of exponent and remaining 23 for mantissa.
So, there is nothing useful you can do with 8bit parts, because they are on their own meaningless.

Resources