1

So, i am trying to program a function which prints a given float number (n) in its (mantissa * 2^exponent) format. I was abled to get the sign and the exponent, but not the mantissa (whichever the number is, mantissa is always equal to 0.000000). What I have is:

unsigned int num = *(unsigned*)&n;
unsigned int m = num & 0x007fffff;
mantissa = *(float*)&m;

Any ideas of what the problem might be?

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
Khosrow
  • 43
  • 1
  • 5

3 Answers3

1

The C library includes a function that does this exact task, frexp:

int expon;
float mant = frexpf(n, &expon);
printf("%g = %g * 2^%d\n", n, mant, expon);

Another way to do it is with log2f and exp2f:

if (n == 0) {
  mant  = 0;
  expon = 0;
} else {
  expon = floorf(log2f(fabsf(n)));
  mant = n * exp2f(-expon);
}

These two techniques are likely to give different results for the same input. For instance, on my computer the frexpf technique describes 4 as 0.5 × 23 but the log2f technique describes 4 as 1 × 22. Both are correct, mathematically speaking. Also, frexp will give you the exact bits of the mantissa, whereas log2f and exp2f will probably round off the last bit or two.


You should know that *(unsigned *)&n and *(float *)&m violate the rule against "type punning" and have undefined behavior. If you want to get the integer with the same bit representation as a float, or vice versa, use a union:

union { uint32_t i; float f; } u;
u.f = n;
num = u.i;

(Note: This use of unions is well-defined in C since roughly 2003, but, due to the C++ committee's long-standing habit of not paying sufficient attention to changes going into C, it is not officially well-defined in C++.)

You should also know IEEE floating-point numbers use "biased" exponents. When you initialize a float variable's mantissa field but leave its exponent field at zero, that gives you the representation of a number with a large negative exponent: in other words, a number so small that printf("%f", n) will print it as zero. Whenever printf("%f", variable) prints zero, change %f to %g or %a and rerun the program before assuming that variable actually is zero.

zwol
  • 135,547
  • 38
  • 252
  • 361
0

In addition to zwol's remarks: if you want to do it yourself you have to acquire some knowledge about the innards of an IEEE-754 float. Once you have done so you can write something like

#include <stdlib.h>
#include <stdio.h>
#include <math.h>               // for testing only

typedef union {
  float value;
  unsigned int bits; // assuming 32 bit large ints (better: uint32_t)
} ieee_754_float;



// clang -g3 -O3 -W -Wall -Wextra -Wpedantic -Weverything -std=c11 -o testthewest testthewest.c -lm
int main(int argc, char **argv)
{

  unsigned int m, num;
  int exp; // the exponent can be negative
  float n, mantissa;
  ieee_754_float uf;

  // neither checks nor balances included!

  if (argc == 2) {
    n = atof(argv[1]);
  } else {
    exit(EXIT_FAILURE);
  }

  uf.value = n;
  num = uf.bits;
  m = num & 0x807fffff;         // extract mantissa (i.e.: get rid of sign bit and exponent)
  num = num & 0x7fffffff;       // full number without sign bit
  exp = (num >> 23) - 126;      // extract exponent and subtract bias
  m |= 0x3f000000;              // normalize mantissa (add bias)
  uf.bits = m;
  mantissa = uf.value;
  printf("n = %g, mantissa = %g, exp = %d, check %g\n", n, mantissa, exp, mantissa * powf(2, exp));

  exit(EXIT_SUCCESS);
}

Note: the code above is one of the quick&dirty(tm) species and is not meant for production. It also lacks handling for subnormal (denormal) numbers, a thing you must include. Hint: multiply the mantissa with a large power of two (e.g.: 2^25 or in that ballpark) and adjust the exponent accordingly (if you took the value of my example subtract 25).

deamentiaemundi
  • 5,502
  • 2
  • 12
  • 20
  • You might also want to add `#ifdef __STDC_IEC_559__` to check if your implementation is using an IEEE-754 float. – Bob__ Apr 12 '18 at 22:14
0

You are stripping off the bits of the exponent, leaving 0. An exponent of 0 is special, it means the number is denormalized and is quite small, at the very bottom of the range of representable numbers. I think you'd find if you looked closely that your result isn't quite exactly zero, just so small that you have trouble telling the difference.

To get a reasonable number for the mantissa, you need to put an appropriate exponent back in. If you want a mantissa in the range of 1.0 to 2.0, you need an exponent of 0, but adding the bias means you really need an exponent of 127.

unsigned int m = (num & 0x007fffff) | (127 << 23);
mantissa = *(float*)&m;

If you'd rather have a fully integer mantissa you need an exponent of 23, biased it becomes 150.

unsigned int m = (num & 0x007fffff) | ((23+127) << 23);
mantissa = *(float*)&m;
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622