Python Forum
replacement for fpformat.extract
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
replacement for fpformat.extract
#1
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.
Gribouillis write Apr-12-2024, 02:43 PM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Reply
#2
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
« We can solve any problem by introducing an extra level of indirection »
Reply
#3
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}")
Output:
-10999999.0 : - 1 09999990 +07 -10234.0 : - 1 02340000 +04 -123.456 : - 1 23456000 +02 -10.4 : - 1 04000000 +01 -0.00137 : - 1 37000000 -03 0.0 : 0 00000000 +00 0.00137 : 1 37000000 -03 10.4 : 1 04000000 +01 123.456 : 1 23456000 +02 10234.0 : 1 02340000 +04 10999999 : 1 09999990 +07
Reply
#4
(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.
# 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

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)
deanhystad write Apr-12-2024, 05:44 PM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Reply
#5
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]
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  String replacement in DB WJSwan 0 766 Dec-28-2022, 05:31 AM
Last Post: WJSwan
Exclamation IndexError: Replacement index 2 out of range for positional args tuple - help? MrKnd94 2 6,464 Oct-14-2022, 09:57 PM
Last Post: MrKnd94
  Extract the largest value from a group without replacement (beginner) preliator 1 2,093 Aug-12-2020, 01:56 PM
Last Post: DPaul
  Simple automated SoapAPI Call with single variable replacement from csv asaxty 1 2,124 Jun-30-2020, 06:38 PM
Last Post: asaxty
  line replacement help mdalireza 8 3,499 Nov-11-2019, 12:54 PM
Last Post: mdalireza
  xml replacement with python josesalazmit 3 8,413 Feb-24-2019, 07:28 PM
Last Post: stullis
  Best replacement for pyzmail in lines 15 and 16 Pedroski55 0 2,504 Nov-03-2018, 06:12 AM
Last Post: Pedroski55
  Is pathlib a viable replacement for os.path? j.crater 4 10,011 Jan-13-2018, 09:49 AM
Last Post: Gribouillis
  Using python for text replacement omar 1 3,869 Dec-22-2016, 01:36 AM
Last Post: ichabod801

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020