5

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?

greydamian
  • 153
  • 2
  • 9
  • I am not sure, but did you figure out if endianness can be a issue or not? – 0xF1 Apr 24 '14 at 10:02
  • Not valid, because size of `unsigned int` may not be equals to size of `float` first, and address in `f` in `bintofloat()` is not basically address of `float`, so `*f` causes undefined behavior. – Grijesh Chauhan Apr 24 '14 at 10:03
  • @GrijeshChauhan : `unsigned int` is always `4` bytes on 32 and 64 bit `gcc` compilers. Isn't it? And `float` as per IEEE 754 standard must be `4` bytes. – 0xF1 Apr 24 '14 at 10:04
  • @MadHatter anyways, sizeof `float*` may not = sizeof `unsigned int*` – Grijesh Chauhan Apr 24 '14 at 10:06
  • @GrijeshChauhan : Both are pointers for `C`, how can then both differ in size. – 0xF1 Apr 24 '14 at 10:07
  • @MadHatter I learn so sometime in past, I didn't find a link except this [Does the size of pointers vary in C?](http://stackoverflow.com/questions/3520059/does-the-size-of-pointers-vary-in-c) – Grijesh Chauhan Apr 24 '14 at 10:11
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/51336/discussion-between-madhatter-and-grijesh-chauhan) – 0xF1 Apr 24 '14 at 10:13
  • 2
    This would usually work, but Strict Aliasing doesn't have to like this so I would suggest `memcpy` (don't worry, it should be optimized away). – harold Apr 24 '14 at 10:27
  • My apologies for the confusion, I should have made this clear. I am aware of the sizing issues of `int` vs. `float` on diff architectures. I chose to stick with built-in types for the sake of simplicity, but were I to implement this properly, I would switch to something like `uint32_t`. – greydamian Apr 24 '14 at 12:46
  • @MadHatter I added an answer. you may like to check. – Grijesh Chauhan Apr 24 '14 at 13:09
  • Better to use `union` than pointers. This takes care of many issues: `float bintofloat(uint32_t x) { union { uint32_t u; float f; } tmp = { x }; assert(sizeof(float)==sizeof(uint32_t)); return tmp.f; }`. – chux - Reinstate Monica Apr 24 '14 at 13:28
  • Endian-ness will not keep code from "working", but code may generate different answers on different platforms. Endian-ness of a `float` is not _required_ to be the same as `int`, but often is the same. Note: `float` is not required to be IEEE and results may vary due to FP encoding. – chux - Reinstate Monica Apr 24 '14 at 13:34
  • @chux Thanks for your comment. I do like the `union` solution. However, I'm still unsure of its potential to handle any alignment issues, as suggested below?! – greydamian Apr 24 '14 at 13:36
  • The `union` will provide proper alignment for the fields and the `assert()` will insure the same size. No pointer casting is involved. – chux - Reinstate Monica Apr 24 '14 at 13:38
  • @chux Thanks. I would generally assume that two variables `x` and `y` would have the same alignment under the assumption `sizeof(x) == sizeof(y)`; I'm not sure how you would sensibly align them differently. However, I'd never read any documentation which actually made this guarantee. – greydamian Apr 24 '14 at 13:46
  • True, `float` _could_ be more than 32 bits (36) and `uint32_t` is padded on some esoteric 36-bit machine, thus their sizes are same, but with padding involved, `bintofloat()` fails to set all bits of the `float`. On such esoteric machines, `uint32_t` may not be defined (hard to implement) and code gladly fails to compile there. IMO, there's no real chance of alignment issue with union, given the code compiles and `uint32_t` and sizes match. I suppose pedantic code would `assert(sizeof (uint64_t) > sizeof(uint32_t)` to insure not compiling on a 64-bit size only for all integer and FP machine. – chux - Reinstate Monica Apr 24 '14 at 16:49

3 Answers3

5

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
Community
  • 1
  • 1
Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
  • Your link to the Stack Overflow answer seems to suggest similar code to what I already have?! Rather than switching from `int` to `float`, it's switching from `float` to `int`, but has the line `p = (int*)&q; /* legal, but ugly */` Do you have an alternate/better recommendation to do this, without using `alignas`, which avoids alignment issues? – greydamian Apr 24 '14 at 12:34
  • By the way, I'm compiling with Clang. And I've compiled with `clang -Weverything...`, but I do not receive any warnings about the casting or alignment. – greydamian Apr 24 '14 at 12:42
  • @greydamian The [link](https://www.securecoding.cert.org/confluence/display/seccode/EXP36-C.+Do+not+cast+pointers+into+more+strictly+aligned+pointer+types) says `float f = (float)x;` is compliant solution. But presently I don't have any idea how would you use this for binary to float conversion. – Grijesh Chauhan Apr 24 '14 at 12:45
  • @greydamian You can do something like [I did binary to float](http://codepad.org/YjiZYYfq) – Grijesh Chauhan Apr 24 '14 at 12:56
  • I did read both links, and the code to which I referred was in [link](http://stackoverflow.com/questions/15790980/what-is-the-difference-between-float-pointer-and-int-pointer-address). Unfortunately, the code `float f = (float)x` does exactly what I'm trying to avoid, as it would "convert" `0x3f800000` to `1065353216.0f`. – greydamian Apr 24 '14 at 13:02
  • @greydamian No `float f = (float)x` is not same as you did in your code, I updated my answer With some code suggestion read. – Grijesh Chauhan Apr 24 '14 at 13:08
  • Thanks for your code. I do like your `union` solution. However, I'm unsure of its potential to handle any alignment issues. Therefore, I'm currently leaning towards the `memcpy` method. – greydamian Apr 24 '14 at 13:30
  • I realise `float f = (float)x` is not what I did in my code. Because that is not the behaviour that I want. As I said, that's "exactly what I'm trying to avoid." – greydamian Apr 24 '14 at 13:41
  • @greydamian Yes, and `memcpy` is smart enough to handle memory alignment issues for us, Hence you can also accomplice same thing using memcpy check [this code at codepad](http://codepad.org/1YNmlj42) – Grijesh Chauhan Apr 24 '14 at 13:47
  • I read your answer, the `union` method is much clean and less disputed. +1. – 0xF1 Apr 24 '14 at 14:35
  • Please do not cite from https://www.securecoding.cert.org/ as if it were normative. – Pascal Cuoq Apr 24 '14 at 18:42
  • @PascalCuoq Ok, I was not aware of this. – Grijesh Chauhan Apr 25 '14 at 06:03
3

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.

Community
  • 1
  • 1
greydamian
  • 153
  • 2
  • 9
  • you can accept your own answer on SO (it is valid). – Grijesh Chauhan Apr 25 '14 at 18:42
  • Thank you, I realise that I can accept my own answer. However, there is a waiting period before I can do so (another 11 hours, at present). What I was attempting to make clear is that it is @harold who deserves credit for this answer, not me. And I that would have accepted his [comment](http://stackoverflow.com/questions/23265265/binary-to-float/23276212#comment35605548_23265265) as the answer, rather than write and accept my own answer, if that were possible. – greydamian Apr 25 '14 at 22:11
  • I din't know to accept our-own answer there is to long wait. (I ever answered my self :P) – Grijesh Chauhan Apr 26 '14 at 03:00
1

First you need to learn how floating point numbers are handled in binary number representation. Follow the standard IEEE 754 format of conversion this should help you get started,

http://www.cprogramming.com/tutorial/floating_point/understanding_floating_point_representation.html

http://www.h-schmidt.net/FloatConverter/IEEE754.html

http://kipirvine.com/asm/workbook/floating_tut.htm

Akash Singh
  • 352
  • 4
  • 5
  • You are referring him to some links, but you didn't mention why? What did you find wrong in OP's implementation? – 0xF1 Apr 24 '14 at 10:23
  • @MadHatter Akash means, how to interpreter binary represent as a float number, So he is suggesting IEEE format that says how floating point numbers are handled in binary number representation. – Grijesh Chauhan Apr 24 '14 at 10:25
  • I appreciate the response; and I do understand IEEE 754 representation, and endianness. However, I don't wish to "interpret" or convert the binary representation in any way, I simply wish to take a sequence of bits and map that on to a float variable for the machine on which the code is being run. – greydamian Apr 24 '14 at 10:46