2

I found the following code from How to get the sign, mantissa and exponent of a floating point number

#include <stdio.h>
typedef union {
  float f;
  struct {
    unsigned int mantisa : 23;
    unsigned int exponent : 8;
    unsigned int sign : 1;
  } parts;
} double_cast;

int main() {
  double_cast d1;
  d1.f = 0.15625;
  printf("sign = %x\n",d1.parts.sign);
  printf("exponent = %x\n",d1.parts.exponent);
  printf("mantisa = %x\n",d1.parts.mantisa);
  return 0;
}

But how do I cast it as a double with the mantissa value being 52, exponent being 11, and sign being 1?

Community
  • 1
  • 1

2 Answers2

2

Just extend the logic and define your union like this, using standard int 64 for portability:

#include <stdint.h>

typedef union {
  double f;
  struct {
    uint64_t mantisa : 52;
    uint64_t exponent : 11;
    uint64_t sign : 1;
  } parts;
} double_cast;

https://en.wikipedia.org/wiki/Double-precision_floating-point_format

BTW: the float mapping should be using the standard types as well instead of int:

typedef union {
  float f;
  struct {
    uint32_t mantisa : 23;
    uint32_t exponent : 8;
    uint32_t sign : 1;
  } parts;
} float_cast;
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • Thanks, what's the declaration for a long? What about for an 8 character value? – OregonState2016 Oct 25 '16 at 21:03
  • 1
    a `long` can be 8 bytes or 4 bytes or whatever, that's why `` is there: to provide unified types with defined size. – Jean-François Fabre Oct 25 '16 at 21:06
  • 1
    That isn't very portable as the C standard doesn't restrict how bit-fields are laid out. – 2501 Oct 25 '16 at 21:08
  • @2501 what isn't very portable? – OregonState2016 Oct 25 '16 at 21:15
  • 1
    @OregonState2016, your whole approach is non-portable. You cannot rely on the unions to map the bitfields to the bits of the `float` / `double` in the way that you hope will happen. – John Bollinger Oct 25 '16 at 21:21
  • @JohnBollinger is there a different approach you suggest? – OregonState2016 Oct 25 '16 at 21:22
  • 3
    @OregonState2016, yes, actually. The `frexp()` family of functions is specifically intended for separating the sign+mantissa and exponent portions of floating-point numbers. It's fairly straightforward to get from there to where you want to be. Don't overlook the fact that a normalized IEEE-754 FP number has an extra implied 1 bit in its mantissa that you may need to account for. – John Bollinger Oct 25 '16 at 21:33
  • @JohnBollinger While `frexp()` and `signbit()` are often an appropriate choice, they can be a little bit less than straightforward if one needs access to the exact *bit pattern* and the floating-point operands include NaNs and denormals, – njuffa Oct 25 '16 at 22:04
  • True enough, @njuffa. The only portable way to directly access the bit pattern of an object of real type is via a `char *` (+/- `signed` / `unsigned`, +/- qualifiers). But there is no portable way to *interpret* the bit pattern accessed that way, since real number representation is not standardized by C. – John Bollinger Oct 25 '16 at 22:15
  • @JohnBollinger If the C compiler supports Appendix F of the ISO-C99 spec (describing IEEE-754 bindings), the bit patterns are standardized. In practice, even without `__STD_IEC_559__` defined the risk of non-portability is tiny since there are exceedingly few platforms with a C compiler that use different floating-point formats (is MIL-STD-1750A still around?) Type-punning via union is sanctioned by ISO-C99, as far as I am aware (this has been covered elsewhere on SO, too lazy to search for the question right now) – njuffa Oct 25 '16 at 22:32
-1

I think you should just use bit and shift operators to extracts the fields from double/float. You can make macro/function for that.

Kajtek
  • 29
  • 4
  • Bitwise operators are applicable only to integer types, not to `float`, `double`, or other real types. – John Bollinger Oct 25 '16 at 21:44
  • Using a pointer to access an object of one type as if it were an object of a different type invokes undefined behavior. There are a few exceptions, but the only one that could apply here would be accessing the real value via a `char *` or `unsigned char *`. That could work, but it would be more complicated than simply shift / mask / done. – John Bollinger Oct 25 '16 at 22:06