replacement for fpformat.extract - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: General Coding Help (https://python-forum.io/forum-8.html) +--- Thread: replacement for fpformat.extract (/thread-41944.html) |
replacement for fpformat.extract - GbSig1998 - Apr-12-2024 I have some python 2.7 code that I need to update to 3.10. The code used fpformat.extract() to determine the following: Python 2.7 Code floatLst=[-10999999.0,-10234.0,-123.456,-10.4,-0.00137,0.0,0.00137,10.4,123.456,10234.0,10999999] fmt = "%.8E" for numFloat in floatLst: sign, intPart, fractPart, exponent = fpformat.extract(fmt % numFloat) print (sign,intPart,fractPart,exponent)The closet equivalent that I've found so far in Python 3.10 uses Decimal. from decimal import * floatLst=[-10999999.0,-10234.0,-123.456,-10.4,-0.00137,0.0,0.00137,10.4,123.456,10234.0,10999999] for numFloat in floatLst: sign,digits,exponent = Decimal(numFloat).as_tuple() print (numFloat,digits,exponent,len(digits))This definitely does not work as well. Due to rounding both digits and exponent can be confusing. Is there another class, or methods of either floats or strings that is specifically designed to replace fpformat.extract()? I have search quite extensively for solutions to this question. Any guidance will be greatly appreciated. RE: replacement for fpformat.extract - Gribouillis - Apr-12-2024 Here is the Python 2 code of fpformat.extract(), you could perhaps convert this function directly, or it may run out of the box in Python 3. # Compiled regular expression to "decode" a number decoder = re.compile(r'^([-+]?)(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$') # \0 the whole thing # \1 leading sign or empty # \2 digits left of decimal point # \3 fraction (empty or begins with point) # \4 exponent part (empty or begins with 'e' or 'E') try: class NotANumber(ValueError): pass except TypeError: NotANumber = 'fpformat.NotANumber' def extract(s): """Return (sign, intpart, fraction, expo) or raise an exception: sign is '+' or '-' intpart is 0 or more digits beginning with a nonzero fraction is 0 or more digits expo is an integer""" res = decoder.match(s) if res is None: raise NotANumber, s sign, intpart, fraction, exppart = res.group(1,2,3,4) intpart = intpart.lstrip('0'); if sign == '+': sign = '' if fraction: fraction = fraction[1:] if exppart: expo = int(exppart[1:]) else: expo = 0 return sign, intpart, fraction, expo RE: replacement for fpformat.extract - deanhystad - Apr-12-2024 Since you know you'll be passing in a number and you know the format, you can make assumptions that could not be made by fpformat.extract() import re values = [-10999999.0, -10234.0, -123.456, -10.4, -0.00137, 0.0, 0.00137, 10.4, 123.456, 10234.0, 10999999] for v in values: s, w, f, e = re.match(r'(-?)(.)\.(.+)e(.*)', f"{v:.8e}").groups() print(f"{v:11} : {s:1} {w:1} {f:8} {e}")
RE: replacement for fpformat.extract - GbSig1998 - Apr-12-2024 (Apr-12-2024, 02:42 PM)Gribouillis Wrote: Here is the Python 2 code of fpformat.extract(), you could perhaps convert this function directly, or it may run out of the box in Python 3. Thank you very much! I was able to convert the code that you posted to Python 3.10 Here is the 3.10 code. I did change a few names to align more with my convention and the name of the function to match its usage. import re # Compiled regular expression to "decode" a number decoder = re.compile(r'^([-+]?)(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$') # \0 the whole thing # \1 leading sign or empty # \2 digits left of decimal point # \3 fraction (empty or begins with point) # \4 exponent part (empty or begins with 'e' or 'E') try: class NotANumber(ValueError): pass except TypeError: NotANumber = 'fpformat.NotANumber' def formatNastranFloat(x): if abs(x) < .01 or abs(x) > 9999999: fmt = "%.8E" else: fmt = "%.8G" s=fmt%x """Return (sign, intPart, fraction, expo) or raise an exception: sign is '+' or '-' intPart is 0 or more digits beginning with a nonzero fraction is 0 or more digits expo is an integer""" res = decoder.match(s) if res is None: raise NotANumber(s) sign, intPart, fractPart, expPart = res.group(1,2,3,4) print (sign, intPart, fractPart, expPart) intPart = intPart.lstrip('0'); if sign == '+': sign = '' if fractPart: fractPart = fractPart[1:] if expPart: expo = int(expPart[1:]) else: expo = 0 # remove trailing zeros in the fraction if fractPart: for i in range(len(fractPart)-1, -1, -1): if fractPart[i] != "0": break fractPart = fractPart[:i+1] # the length of the fractional parts is whatever is left over fracLen = 8 - len(sign) - len(intPart) - 1 - len(str(expo)) s = (sign + intPart + "." + fractPart[:fracLen] + str(expo)).ljust(8) return s,sign, intPart, fractPart, expo if __name__=='__main__': myFloat=-0.000124 s,sign, intPart, fraction, expo=formatNastranFloat(myFloat) print (s,sign, intPart, fraction, expo) RE: replacement for fpformat.extract - deanhystad - Apr-12-2024 I don't think your code works. Test with your numbers from the first post: floatLst=[-10999999.0,-10234.0,-123.456,-10.4,-0.00137,0.0,0.00137,10.4,123.456,10234.0,10999999] |