Search code examples
cperformancemathdouble

Calculating decimal exponent of double quickly and accurately


edit: To clarify, by "decimal exponent of a double" I mean the magnitude of the value or the base 10 exponent of the value. For instance 3456.7 -> 3.4567E3 -> Scientific Exponent = 3

I need to find the decimal exponent of a double.

I've been researching ways this is done, and I've found various methods.

For instance to estimate the exponent Microsoft's STL uses an interesting lookup table:

However, the method I came up with seems the simplest:

  // v -> value
  // x -> ScientificExponent
  // 10^x <= v < 10^(x+1)
  // x <= log(v) < x+1

  ScientificExponent = (Value == 0) ? 1 : floor((log10(fabs(Value))));

I'm wondering why this isn't used. Are there cases where this doesn't work? Is this assumed to be too slow?

Are there any edge/corner cases I am not considering (maybe subnormal values being too small to get an accurate log calculation from)?

I'd appreciate any insight people can provide.

Also, if this is a good way to do this, is there a good way to calculate only the integer part of log10? It seems a waste to calculate the log to many decimal places only to throw it away with the floor.


Solution

  • floor((log10(fabs(Value)))) is inadequate to compute the decimal exponent of a number.

    Consider using IEEE-754 binary64 (“double precision”). Let Value be 0.09999999999999999167332731531132594682276248931884765625 = 9.999999999999999167332731531132594682276248931884765625•10−2, which is a representable value.

    log10 of 0.09999999999999999167332731531132594682276248931884765625 is approximately ?−1.000000000000000036162279995748268157562864421389843161585017393347605917054938808868324457086236340. This number is not representable in binary64; the closest representable value is −1.

    So a good log10 implementation will return −1 for input 0.09999999999999999167332731531132594682276248931884765625.

    Then floor((log10(fabs(Value)))) produces −1, but the correct result would be −2.