Last active
January 7, 2024 06:24
-
-
Save libsteve/753c31c49f26a4c08aa3e0448b6911bc to your computer and use it in GitHub Desktop.
Get an integer mantissa for floating-point values in C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// `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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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