简体   繁体   中英

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

Are there functions in the Python library or numpy that take a float as input and return its decimal scientific notation decomposition, ie 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.

eg 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 .

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 .

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)

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM