Skip to content

Instantly share code, notes, and snippets.

@stevebrun
Last active January 7, 2024 06:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevebrun/753c31c49f26a4c08aa3e0448b6911bc to your computer and use it in GitHub Desktop.
Save stevebrun/753c31c49f26a4c08aa3e0448b6911bc to your computer and use it in GitHub Desktop.
Get an integer mantissa for floating-point values in C
#include "iexp.h"
#include <float.h>
#include <math.h>
#include <stddef.h>
// https://stackoverflow.com/questions/7812044/finding-trailing-0s-in-a-binary-number/36791297#36791297
// Manually get two's compliment with (~integer + 1) instead of arithmatic
// negation because signed integer representation is implementation-defined
// behavior, so we can't always assume that negative integers are represented
// with two's complement instead of one's complement or signed magnitude.
#define trailing_zeros(x) log2((x) & (~(x) + 1))
// References:
// https://stackoverflow.com/questions/5672960/how-can-i-extract-the-mantissa-of-a-double/5673518#5673518
// https://stackoverflow.com/questions/15685181/how-to-get-the-sign-mantissa-and-exponent-of-a-floating-point-number/19608878#19608878
// https://stackoverflow.com/questions/49804055/getting-the-mantissa-of-a-float-of-either-an-unsigned-int-or-a-float-c/49804333#49804333
// https://stackoverflow.com/questions/72659156/convert-double-to-integer-mantissa-and-exponents/72659705#72659705
float iexpf(float x, int *exponent) {
// Use frexpf() to get the fractional mantissa and exponent
float const mantissa = frexpf(x, exponent);
// Use ldexpf() with FLT_MANT_DIG to get an integer mantissa
long scaled_mantissa = (long)ldexpf(mantissa, FLT_MANT_DIG);
// Count trailing zeros in integer mantissa to get a good scaling value
size_t const trailing_zeros = trailing_zeros(scaled_mantissa);
size_t const mantissa_scale = FLT_MANT_DIG - trailing_zeros;
// Adjust the exponent for the scaled mantissa
if (exponent) {
*exponent -= mantissa_scale;
}
// Call ldexpf() again instead of bit-shifting the integer representation
// to avoid relying on implementation-defined behavior when it comes to the
// representation of negative integers and their bit-shifting behavior (i.e.
// whether or not shifting a negative integer yields a negative integer)
return ldexpf(mantissa, mantissa_scale);
}
#ifdef iexp
#undef iexp
#endif
double iexp(double x, int *exponent) {
// Use frexp() to get the fractional mantissa and exponent
double const mantissa = frexp(x, exponent);
// Use ldexp() with DBL_MANT_DIG to get an integer mantissa
long long scaled_mantissa = (long long)ldexp(mantissa, DBL_MANT_DIG);
// Count trailing zeros in integer mantissa to get a good scaling value
size_t const trailing_zeros = trailing_zeros(scaled_mantissa);
size_t const mantissa_scale = DBL_MANT_DIG - trailing_zeros;
// Adjust the exponent for the scaled mantissa
if (exponent) {
*exponent -= mantissa_scale;
}
// Call ldexp() again instead of bit-shifting the integer representation
// to avoid relying on implementation-defined behavior when it comes to the
// representation of negative integers and their bit-shifting behavior (i.e.
// whether or not shifting a negative integer yields a negative integer)
return ldexp(mantissa, mantissa_scale);
}
long double iexpl(long double x, int *exponent) {
// Use frexpl() to get the fractional mantissa and exponent
long double const mantissa = frexpl(x, exponent);
// Use ldexpl() with LDBL_MANT_DIG to get an integer mantissa
long long scaled_mantissa = (long long)ldexpl(mantissa, LDBL_MANT_DIG);
// Count trailing zeros in integer mantissa to get a good scaling value
size_t const trailing_zeros = trailing_zeros(scaled_mantissa);
size_t const mantissa_scale = LDBL_MANT_DIG - trailing_zeros;
// Adjust the exponent for the scaled mantissa
if (exponent) {
*exponent -= mantissa_scale;
}
// Call ldexpl() again instead of bit-shifting the integer representation
// to avoid relying on implementation-defined behavior when it comes to the
// representation of negative integers and their bit-shifting behavior (i.e.
// whether or not shifting a negative integer yields a negative integer)
return ldexpl(mantissa, mantissa_scale);
}
// `frexp()` stands for "fraction and exponent".
// `iexp()` stands for "integer and exponent".
float iexpf(float f, int *exponent);
double iexp(double f, int *exponent);
long double iexpl(long double f, int *exponent);
#if __STDC_VERSION__ >= 201112L
#define iexp(f, exponent) _Generic((f), \
float: iexpf(f, exponent), \
double: iexp(f, exponent), \
long double: iexpl(f, exponent) \
)
#endif
static inline long liexpf(float f, int *exponent) {
return (long)iexpf(f, exponent);
}
static inline long liexp(double f, int *exponent) {
return (long)iexp(f, exponent);
}
static inline long liexpl(long double f, int *exponent) {
return (long)iexpl(f, exponent);
}
#if __STDC_VERSION__ >= 201112L
#define liexp(f, exponent) _Generic((f), \
float: liexpf(f, exponent), \
double: liexp(f, exponent), \
long double: liexpl(f, exponent) \
)
#endif
static inline long long lliexpf(float f, int *exponent) {
return (long long)iexpf(f, exponent);
}
static inline long long lliexp(double f, int *exponent) {
return (long long)iexp(f, exponent);
}
static inline long long lliexpl(long double f, int *exponent) {
return (long long)iexpl(f, exponent);
}
#if __STDC_VERSION__ >= 201112L
#define lliexp(f, exponent) _Generic((f), \
float: lliexpf(f, exponent), \
double: lliexp(f, exponent), \
long double: lliexpl(f, exponent) \
)
#endif
#include <stdio.h>
#include "iexp.h"
void print_components(double f) {
int e = 0;
long long m = lliexp(f, &e);
printf("%f\t= %lld * 2^%d\n", f, m, e);
}
int main(int argc, char **argv) {
print_components(0.15625); // 0.156250 = 5 * 2^-5
print_components(-0.15625); // -0.156250 = -5 * 2^-5
print_components(10.52178); // 10.521780 = 1480808890227323 * 2^-47
print_components(3.14159); // 3.141590 = 3537115888337719 * 2^-50
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment