std::frexp
is a function that removes exponent from a floating point variable and puts it into a variable exp
of type int
. It uses int
regardless of how many bits the exponent really needs and according to the linked page:
If the value to be stored in
*exp
is outside the range ofint
, the behavior is unspecified.
So, how do I check whether "*exp
is outside the range of int
"?
I am thinking about adding a static_assert
to my code that compares FLT_MIN_EXP
& FLT_MAX_EXP
to INT_MIN
& INT_MAX
.
However, I'm afraid of making an off-by-one error because I don't fully comprehend descriptions of these constants.
FLT_MIN_EXP
:
minimum negative integer such that FLT_RADIX raised by power one less than that integer is a normalized
float
...
FLT_MAX_EXP
:
maximum positive integer such that FLT_RADIX raised by power one less than that integer is a representable finite
float
...
(I already have static_assert(FLT_RADIX == 2);
in the code so radix equal to 10 is not a concern in my case.)
Assertion 1: This operation usually works fine, and you don't need to worry about anything.
Assertion 2: For peace of mind, if there's every a system where this doesn't work, we want to know about it. We'll revisit a solution when we find one of those systems.
Assertion 3: The largest and smallest exponents returned from std::frexp
can be represented by the system's int
type if the operation is reversible.
You reverse std::frexp
by its complement std::ldexp
.
Note that if int
cannot hold a floating point's exponent, the result is not undefined (it's merely unspecified), so we can rely on the test being well formed.
TEST_CASE("The largest exponent returned by std::frexp can be represented by int") {
long double largest_ld = std::numeric_limits<long double>::max();
int exponent;
long double fraction = std::frexp(largest_ld, &exponent);
errno = 0;
REQUIRE(std::ldexp(fraction, exponent) == largest_ld);
REQUIRE(errno == 0); // just in case?
}
TEST_CASE("The smallest exponent returned by std::frexp can be represented by int") {
long double smallest_ld = std::numeric_limits<long double>::min();
int exponent;
long double fraction = std::frexp(smallest_ld, &exponent);
errno = 0;
REQUIRE(std::ldexp(fraction, exponent) == smallest_ld);
REQUIRE(errno == 0); // just in case?
}
Probably, but I'm having trouble understanding the same part of the language you were having trouble with, so I'm optimizing developer speed over compile time checks.
As a first order approximation, may I suggest:
static_assert(sizeof(int) >= 32);
static_assert(std::numeric_limits<long double>::is_eec559);
And of course, revisit when you hit a platform where this isn't true.