Decompose a float into mantissa and exponent in base 10 without strings

Question:

Are there functions in the Python library or numpy that take a float as input and return its decimal scientific notation decomposition, i.e. mantissa and exponent? Or is there a BRIEF way to accomplish this without resorting to string conversions or using a for loop to determine the exponent? Writing such a function wouldn’t be difficult, I’m just shocked that I’m having trouble finding an existing one in math, decimal or numpy.

e.g. if fexp and fman are the functions giving the exponent and mantissa of the decimal floating point representation of a float then we’d expect the following statements to all return true:

fexp(154.3) == 2.0
fman(154.3) == 1.543
fexp(-1000) == 3.0
fman(-1000) == -1.0

In short, this would be a “decimal version” of math.frexp.

Asked By: jpm

||

Answers:

I’m hoping there’s a better answer but I came up with

from math import floor, log10

def fexp(f):
    return int(floor(log10(abs(f)))) if f != 0 else 0

def fman(f):
    return f/10**fexp(f)
Answered By: jpm

One way to avoid string conversions is to implement the methods using Decimals:

from decimal import Decimal

def fexp(number):
    (sign, digits, exponent) = Decimal(number).as_tuple()
    return len(digits) + exponent - 1

def fman(number):
    return Decimal(number).scaleb(-fexp(number)).normalize()

Note that using floating point numbers, its not possible to calculate mantissa and exponent without rounding. The reason is that floating point numbers are stored as base 2 fractions. For example stored float value for 154.3 is 154.30000000000001136868377216160297393798828125. Floats are displayed in console as accurate numbers, because (in CPython) they are always rounded when serialized using a hard-coded precision of 17.

Answered By: jsalonen

ChatGPT gave me some clues about this way…

I think it is pretty elegant, but seems to break around 1e-324

import math
def man(value:float, significant_digits_int=4):
    return float(f"{value:.{significant_digits}e}".split('e')[0]) if math.isfinite(value) else float('nan')
def exp(value:float, significant_digits_int=4):
    return int(f"{value:.{significant_digits}e}".split('e')[1]) if math.isfinite(value) else float('nan')
def man_exp(value:float, significant_digits_int=4):
    return man(value, significant_digits), exp(value, significant_digits)

print(man_exp(.000006748439390))
print(man_exp(-3456.67858543, 5))
print(man_exp(.000006748439390,100))
print(man_exp(float('nan')))
print(man_exp(float('inf')))
print(man_exp(float('-inf')))
print(man_exp(0))
print(man_exp(0,6))
print(man_exp(1.25e-12,6))
print(man_exp(1.25e-308,6))
print(man_exp(1.25e-323,6))
print(man_exp(1.25e-324,6))
print(math.isfinite(1.25e-324))
(6.7484, -6)
(-3.45668, 3)
(6.74843939, -6)
(nan, nan)
(nan, nan)
(nan, nan)
(0.0, 0)
(0.0, 0)
(1.25, -12)
(1.25, -308)
(1.482197, -323)
(0.0, 0)
True
Answered By: Thon Deboer
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.