1

I tried to implement float_twice(float_bits x) in C, which multiplies x by 2 in bit-level. I compared the result of float_twice and the real float multiplication implemented in C, but found a difference.

float_twice(0X800001) yields 0X1000001, and this seems correct to me. As long as I understand, 0X80001 is a floating point representation of exponent 1, and fractional part 1. Since it is a normalized value, I believe adding one to the exponent part is sufficient.

However, ((float) 0X800001) * 2.0 yields 0X1000002.

This seems to be a multiplication of unsigned integers, not float values.

To summarize, these are my questions,

  1. What is the correct output for float_twice(0X800001)?

  2. If the correct output is 0X1000001, why does C compute ((float) 0X800001) * 2.0 as 0X1000002?

Barmar
  • 741,623
  • 53
  • 500
  • 612
Kyle
  • 13
  • 2
  • 3
    `(float) 0X800001` means to convert the integer `0x800001` to the floating point number with the closest numeric value, it doesn't interpret the bit pattern as a floating point number. – Barmar Sep 17 '19 at 16:21
  • 1
    E.g. `(float) 1 == 1.0` – Barmar Sep 17 '19 at 16:22
  • You might want to look at [this code](https://github.com/carlosgaldino/cs-app/blob/master/ch02/float_half.c), which halves a floating-point number rather than doubling it. But be aware that (i) the code is very non-portable; (ii) it doesn't cover any special cases like NANs; and (iii) it handles denormalised numbers, but not overflows (which of course never occur when you halve a number). – TonyK Sep 17 '19 at 16:33
  • Thank you very much. Now I understand the problem in my code. I should use something like reinterpret_cast, right? – Kyle Sep 17 '19 at 16:39
  • @Kang There is no such thing as `reinterperet_cast` in C. – S.S. Anne Sep 17 '19 at 18:10

1 Answers1

2

Your test is incorrect: ((float) 0X800001) converts the integer value 0x800001 to the closest float value, not to a float with the same bit representation.

To check your results, you must perform type punning, which can be done portably with memcpy:

#include <stdint.h>
#include <string.h>

typedef uint32_t float_bits;

float_bits check_twice(float_bits x) {
    float f;
    float_bits r;
    memcpy(&f, &x, sizeof f);
    f *= 2;
    memcpy(&r, &f, sizeof r);
    return r;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    Aha! You caught on to the `memcpy` thing that I've been spreading around. – S.S. Anne Sep 17 '19 at 18:11
  • 1
    @JL2210: I'm sorry to admit it. `f = *(float *)&x` is much more explicit... but cannot be used because of the strict aliasing rule. – chqrlie Sep 17 '19 at 18:14
  • @chqrlie: Cannot be used because it's not defined. There's no "strict aliasing rule" that says you can't do this. It's just never been a defined behavior. – R.. GitHub STOP HELPING ICE Sep 17 '19 at 18:19
  • R.: *It's just never been a defined behavior* is quite a bold statement! The strict aliasing rules makes it undefined since Ansi C in 1990, to allow for better optimisations, which sometimes seem counter-intuitive. Too bad Dennis R. cannot tell us if he intended for `f = *(float *)&x` to behave as expected in the early days of the C language... His *C Reference Manual* from the seventies, before the cast syntax was invented, shows much worse type punning approaches: https://www.bell-labs.com/usr/dmr/www/cman.pdf pp23,24 – chqrlie Sep 17 '19 at 18:46