I am interested in writing a fast C program that flips the exponent of a double. For instance, this program should convert 1e300 to 1e-300. I guess the best way would be some
Assuming you mean to negate the decimal exponent, the power of ten exponent in scientific notation:
#include <math.h>
double negate_decimal_exponent(const double value)
{
if (value != 0.0) {
const double p = pow(10.0, -floor(log10(fabs(value))));
return (value * p) * p;
} else
return value;
}
Above, floor(log10(fabs(value)))
is the base 10 logarithm of the absolute value of value
, rounded down. Essentially, it is the power of ten exponent in value
using the scientific notation. If we negate it, and raise ten to that power, we have the inverse of that power of ten.
We can't calculate the square of p
, because it might underflow for very large values of value
in magnitude, or overflow for very small values of value
in magnitude. Instead, we multiply value
by p
, so that the product is near unity in magnitude (that is, decimal exponent is zero); then multiply that with p
, to essentially negate the decimal exponent.
Because the base-ten logarithm of zero is undefined, so we need to deal with that separately. (I initially missed this corner case; thanks to chux for pointing it out.)
Here is an example program to demonstrate:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
double negate_decimal_exponent(const double value)
{
if (value != 0.0) {
const double p = pow(10.0, -floor(log10(fabs(value))));
return (value * p) * p;
} else
return value;
}
#define TEST(val) printf("negate_decimal_exponent(%.16g) = %.16g\n", val, negate_decimal_exponent(val))
int main(void)
{
TEST(1.0e300);
TEST(1.1e300);
TEST(-1.0e300);
TEST(-0.8e150);
TEST(0.35e-25);
TEST(9.83e-200);
TEST(23.4728395e-220);
TEST(0.0);
TEST(-0.0);
return EXIT_SUCCESS;
}
which, when compiled (remember to link with the math library, -lm
) and run, outputs (on my machine; should output the same on all machines using IEEE-754 Binary64 for double
s):
negate_decimal_exponent(1e+300) = 1e-300
negate_decimal_exponent(1.1e+300) = 1.1e-300
negate_decimal_exponent(-1e+300) = -1e-300
negate_decimal_exponent(-8e+149) = -8e-149
negate_decimal_exponent(3.5e-26) = 3.5e+26
negate_decimal_exponent(9.83e-200) = 9.83e+200
negate_decimal_exponent(2.34728395e-219) = 2.34728395e+219
negate_decimal_exponent(0) = 0
negate_decimal_exponent(-0) = -0
Are there faster methods to do this?
Sure. Construct a look-up table of powers of ten, and use a binary search to find the largest value that is smaller than value
in magnitude. Have a second look-up table have the two multipliers that when multiplied with value
, negates the decimal power of ten. Two factors are needed, because a single one does not have the necessary range and precision. (However, the two values are symmetric with respect to the base-ten logarithm.) For a look-up table with thousand exponents (covers IEEE-754 doubles, but one should check at compile time that it does cover DBL_MAX
), that would be ten comparisons and two multiplications (using floating-point values), so it'd be quite fast.
A portable program could calculate the tables necessary at run-time, too.