I am writing an input file to a program with roots in the 60s, and it reads data from fixed-width data fields on text files. The format is:
'.'
or be written on exponential format, e.g. '1.23e8'
The closest i've gotten is
print "{0:8.3g}".format(number)
which yields '1.23e+06'
with 1234567
, and ' 1234'
with 1234
.
I would like to tweak this however, to get
'1234567.'
with 1234567
(i.e. not going to exponential format before it is
required), ' 1234.'
with 1234
(i.e. ending with a dot so it is not interpreted as an integer), '1.235e+7'
with 12345678
(i.e. using only one digit for the exponent), '-1.23e+7'
with -1234567
(i.e. not violating the 8 digit maximum on
negative numbers).Since this is (as far as I recall) easily achievable with Fortran and the problem probably comes up now and then when interacting with legacy code I suspect that there must be some easy way to do this?
I simply took the answer by @Harvey251 but split into test part and the part we need in production.
Usage would be:
# save the code at the end as formatfloat.py and then
import formatfloat
# do this first
width = 8
ff8 = formatfloat.FormatFloat(width)
# now use ff8 whenever you need
print(ff8(12345678901234))
And here is the solution. Save the code as formatfloat.py and import it to use FlotFormat class. As I said below, loop part of calculation better be moved to init part of the FormatFlot class.
import unittest
class FormatFloat:
def __init__(self, width = 8):
self.width = width
self.maxnum = int('9'*(width - 1)) # 9999999
self.minnum = -int('9'*(width - 2)) # -999999
def __call__(self, x):
# for small numbers
# if -999,999 < given < 9,999,999:
if x > self.minnum and x < self.maxnum:
# o = f'{x:7}'
o = f'{x:{self.width - 1}}'
# converting int to float without adding zero
if '.' not in o:
o += '.'
# float longer than 8 will need rounding to fit width
elif len(o) > self.width:
# output = str(round(x, 7 - str(x).index(".")))
o = str(round(x, self.width-1 - str(x).index('.')))
else:
# for exponents
# added a loop for super large numbers or negative as "-" is another char
# Added max(max_char, 5) to account for max length of less
# than 5, was having too much fun
# TODO can i come up with a threshold value for these up front,
# so that i dont have to do this calc for every value??
for n in range(max(self.width, 5) - 5, 0, -1):
fill = f'.{n}e'
o = f'{x:{fill}}'.replace('+0', '+')
# if all good stop looping
if len(o) == self.width:
break
else:
raise ValueError(f"Number is too large to fit in {self.width} characters", x)
return o
class TestFormatFloat(unittest.TestCase):
def test_all(self):
test = (
("1234567.", 1234567),
("-123456.", -123456),
("1.23e+13", 12345678901234),
("123.4567", 123.4567),
("123.4568", 123.45678),
("1.234568", 1.2345678),
("0.123457", 0.12345678),
(" 1234.", 1234),
("1.235e+7", 12345678),
("-1.23e+6", -1234567),
)
width = 8
ff8 = FormatFloat(width)
for expected, given in test:
output = ff8(given)
self.assertEqual(len(output), width, msg=output)
self.assertEqual(output, expected, msg=given)
if __name__ == '__main__':
unittest.main()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With