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.)
CodePudding user response:
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.
Write a unit test that tests if this is reversible
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?
}
Can you do better with compile time checks?
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.