2

I have an uint64_t and I want to store in it, as its rightmost 32 bits, a float.

Basically, what I want is, given a float f:

   |--------------------------------|--------------------------------|
   |         32 bits set to 0       |    the 32 bits of our float    |
   |================================|================================|
   ^                                                                 ^
   ^                      64-bit unsigned integer                    ^
   |-----------------------------------------------------------------|

I've tried casting it (uint_64t)f, but the results are a bit weird.

Any ideas?

Dr.Kameleon
  • 22,532
  • 20
  • 115
  • 223
  • [How do I display the binary representation of a float or double?](https://stackoverflow.com/q/397692/995714), [extract bits from 32 bit float numbers in C](https://stackoverflow.com/q/11136408/995714), [Obtaining bit representation of a float in C](https://stackoverflow.com/q/44609743/995714) – phuclv Dec 27 '19 at 13:20
  • 1
    FYI, this is not a conversion. A conversion changes the representation of something while preserving the value. (Ideally, the value is preserved exactly, but it may be altered if necessary.) For example, converting the `int` 3 to `char` yields a `char` of value 3—the representation has a different number of bits but the value is the same. Converting it to a `float` also yields 3 but with very different bits representing it. Converting 3 kg to pounds yields 6.6 lb.—3 kg and 6.6 lb. are the same thing. Converting one pointer to another pointer type yields a pointer to the same place… – Eric Postpischil Dec 27 '19 at 13:46
  • 1
    … What you seem to be seeking is an operation that puts the bits that represent a `float` into the low 32 bits of a `uint64_t`. That is not a conversion. (Some conversions that do not preserve the exact value include floating-point to integer—converting 3.25 to integer yields 3. It is an imperfect conversion, but the value is preserved to the extend possible. Converting −1 to unsigned yields a large positive value, but they have some mathematical equivalencies.) – Eric Postpischil Dec 27 '19 at 13:47
  • Examples of input `float` and output `uint64_t` would help clarify what your really are trying to do. Do you care that the result is not portable across machines? By "rightmost " to you mean the least significant bytes or the highest addressed bytes? Vote as unclear as to what is wanted. – chux - Reinstate Monica Dec 27 '19 at 16:31

2 Answers2

5

Assuming you want the uint64_t to contain the representation of the float, you would do the following:

uint32_t t;
memcpy(&t, &f, sizeof(t));
uint64_t val = t;

The call to memcpy copies the representation of the float to a uint32_t which is the same size. Then the assignment that follows effectively sets the high order word of val to 0 and the low order word to the 32 bit value. Performed this way, it doesn't depend on endianness.

dbush
  • 205,898
  • 23
  • 218
  • 273
-1

Edit: Though I do justify the comments below, this edit has been added in case someone needs to tweak / shuffle bytes in C.

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

int main()
{
    // test vars
    unsigned int testInt = 1;
    char *testChar = (char *)&testInt;

    // allocate an 64 bit unsigned integer set all bits to zero
    uint64_t i = 0;

    // define the float here
    float f = 3.14;

    printf("64 bits integer val before copy = %"PRId64"\n", i);
    printf("32 bits float val before copy   = %f\n", f);

    if (*testChar == 1) {
        // little endian
        printf("little endian system\n");
        *((float *)&i) = f; // copy float bit pattern to lower bytes
    }
    else {
        // big endian
        printf("big endian system\n");
        *((float *)&i + 1) = f; // copy float bit pattern to lower bytes
    }

    printf("64 bits integer val after copy  = %"PRId64"\n", i);

    return 0;
}
ssd
  • 2,340
  • 5
  • 19
  • 37
  • The violates C 2018 6.5 7: “An object shall have its stored value accessed only by an lvalue expression that has one of the following types…” Basically, `i` is a `uint64_t`, but this code access it as a `float`, which is not one of the types listed in 6.5 7. Some compilers support it, and I favor using compiler extensions and non-standard C when useful, but there is no need for it in this case because there are simple portable ways of accomplishing the goal, such as the `memcpy` shown in the other answer or using a union (supported in standard C but not in C++). – Eric Postpischil Dec 27 '19 at 14:14
  • Additionally, the other answer shows how to move the bits to the desired low 32 bits without endianness issues. Although this answer notes that endianness can be an issue, what value is there in entering an answer that fails to provide a feature already provided in another answer, with portable code to boot? – Eric Postpischil Dec 27 '19 at 14:15
  • 1
    This code also potentially invokes undefined behavior per [**6.3.2.3 Pointers**, paragraph 7 of the C11 standard](https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p7): "A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined." Some implementations will throw `SIGBUS` or similar when you run code like this. – Andrew Henle Dec 27 '19 at 14:49