Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binary to Float

I wanted to produce float variables from their binary representation, and I quickly came up with the following code:

float bintofloat(unsigned int x) {
    float *f = (float *)&x;
    return *f;
}

The above function can then be called as follows:

float f = bintofloat(0x3f800000);

I was just hoping to get some opinions on this approach: whether using pointers is the best method, or whether there is a better or more concise method?

Unfortunately, my use of types seems to have caused a distraction. I apologise for this; I chose to stick with built-in types for the sake of simplicity. But I do recognise that it is naive, and assumes that sizeof(int) == sizeof(float).

Again, my primary question is whether the use of pointers is a good way to achieve this translation from binary to floating-point?

like image 901
greydamian Avatar asked Mar 19 '23 19:03

greydamian


2 Answers

In your code, in function bintofloat() following address casting and dereferencing is invalid:

float *f = (float *)&x;
return *f;

This will call undefined behavior at runtime.

EXP36-C. Do not cast pointers into more strictly aligned pointer types

Do not convert a pointer value to a pointer type that is more strictly aligned than the referenced type. Different alignments are possible for different types of objects. If the type-checking system is overridden by an explicit cast or the pointer is converted to a void pointer (void *) and then to a different type, the alignment of an object may be changed.

The C Standard, 6.3.2.3, paragraph 7 [ISO/IEC 9899:2011], states:

A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.

If the misaligned pointer is dereferenced, the program may terminate abnormally. On some architectures, the cast alone may cause a loss of information even if the value is not dereferenced if the types involved have differing alignment requirements.

In your code x is unsigned int that has different memory alignments then float types.

Type conversion float *f = (float *)&x; is valid but not good idea in general. A float type pointer should point to float type. Some systems may even give the program a fatal SIGBUS when deferencing an unsigned int* as a float* because float require more strict address alignment to a multiple of sizeof(float).

When you what to write generic code then approved way to assign address of unknown type is using void*, but to derefrence you have to convert into correct type back so in fact following code is wrong:

unsigned int x = 10U;
unsigned int *xp = &x; 
void* vp = x;
float* fp = vp;
float f = *fp;

The link from where I quoted give you some more examples that will help you to understand the issue.

You should also read this Keith Thompson's answer: What is the difference between float pointer and int pointer address?

Edit: I think you can do something like as below:

#include<stdio.h>
float bintofloat(unsigned int x) {
    union {
        unsigned int  x;
        float  f;
    } temp;
    temp.x = x;
    return temp.f;
}

int main(){
    float f = bintofloat(0x4236AE14U);
    printf ("\nf = %f ", f);
    printf ("\nf = %f \n", bintofloat(0x4287F0A4U));    
    return 0;
}

Use it as:

$ gcc -Wall -pedantic binarytofloat.c
taxspanner@:~$ ./a.out 

f = 45.669998 
f = 67.970001 

Check following float Precision IEEE754 calculator's Links:

  1. 0x4236AE14U
  2. 0x4287F0A4U
like image 108
Grijesh Chauhan Avatar answered Mar 22 '23 10:03

Grijesh Chauhan


I thought I should document that my preferred answer is to use memcpy, as suggested by @harold in this comment. Unfortunately, I'm unable to directly accept a comment as being the answer to my question.

For reference, a solution using memcpy may look something like the following:

float bintofloat(uint32_t x) {
    float f = 0.0f;
    memcpy(&f, &x, sizeof(f) < sizeof(x) ? sizeof(f) : sizeof(x));
    return f;
}

Although the memcpy solution is my preferred answer, I'd also like to recognise that another nice solution was suggested which makes use of a union: here and here.

like image 40
greydamian Avatar answered Mar 22 '23 10:03

greydamian