1

What I'm trying to accomplish

-12.345 -> -012.345

Where left of decimal can be -999->999 (for my specific need) and n-amount of decimal digits

I came up with two methods to do this:

def pad(num, pad):
    pad = pow(10,pad)
    if num > 0:
        return "%s" %  str(num + pad)[1:]
    else:
        return "-%s" % str(num - pad)[2:]

def pad(num, pad):
    import math
    mag  = 1 + int(math.log10(abs(num)))
    sz   = len(str(num))
    return str(num).zfill(pad+sz-mag)

but this seems rather obtuse for being python. I saw a previous similar question but didn't like the answer ...

>>> "%06.2f"%3.3
'003.30'

because it assumes you already know where your decimal point is and how many decimal digits you have.

This seems like a common situation, so is there an existing better/cleaner/single fncall, way to go about this in python 2.7?

pyInTheSky
  • 1,459
  • 1
  • 9
  • 24
  • I believe [```str.format()```](https://docs.python.org/2/library/string.html#formatspec) is what you want – wnnmaw Jun 05 '14 at 20:28
  • http://stackoverflow.com/questions/17118071/python-add-leading-zeroes-using-str-format looks like it works nicely with ints but not floats since my width is variable depending on the amount of decimal digits, so from what I can tell, I'd still need to count the number of decimal digits I have :\ – pyInTheSky Jun 05 '14 at 20:33
  • 2
    @pyInTheSky: what do you mean *amount of decimal digits*? Floats are *approximations*, you invariably end up rounding *anyway*. `-12.345` is not *exactly* `-12.345` either, for example. `format(-12.345, '.53f')` > `'-12.34500000000000063948846218409016728401184082031250000'`. – Martijn Pieters Jun 05 '14 at 20:37
  • So you want a fixed width of 3 on the left and variable width on the right? – user2357112 Jun 05 '14 at 20:40
  • @user2357112 that is correct – pyInTheSky Jun 05 '14 at 20:48

3 Answers3

2

I think you have found a corner case. It's easy to specify digits to the left and to the right of the decimal place; it's also easy to specify total number of digits; it's not very easy to specify digits to the left and leave digits to the right completely unspecified.

I suggest that the cleanest thing to do is to just take the integer portion of the float and format that, then take the fraction and format that (not specifying a length).

Here's a tested function that does what you asked for, and also works correctly for negative numbers.

import math

def float_lformat(f, width):
    neg = f < 0.0
    fraction, n = math.modf(abs(f)) # get fraction and integer parts of f
    if neg and width > 0:
        width -= 1
    s = "%0*d" % (width, n) + str(fraction)[1:]
    if neg:
        s = '-' + s
    return s
steveha
  • 74,789
  • 21
  • 92
  • 117
  • Yeah, I'm taking a NMEA lat. value that's been converted from str to float (losing it's leading 0 for lats < 100) and then formatting it back later on, so not the most common way to display numbers. – pyInTheSky Jun 05 '14 at 20:59
1

Not sure if this is pythonic. It just pad integer and concat the decimal.

def pad(x, p):
    return ('%0'+str(p+(x<0 and 1 or 0))+'d%s') % (x,str(x-int(x))[x<0 and 2 or 1:])

tests

pad(-3,3) -> -003
pad(3,3) -> 003
pad(-3.3,3) -> -003.3
pad(3.3,3) -> 003.3
Fabricator
  • 12,722
  • 2
  • 27
  • 40
  • A bummer that there doesn't seem to be a pretty built in way, but b.a. for some crazy splicing logic. I did not know that that could be done. – pyInTheSky Jun 06 '14 at 13:39
0

Fixed vs. dynamic fractions:

>>> "{:08}".format(-12.345)
'-012.345'

>>> a,b=str(x).split('.');a=int(a);"{}{:03}.{}".format('-' if a<0 else"",abs(a),b)
'-012.345'

Cleaned up:

def pad(x):
    a, b = str(x).split('.')
    a = int(a)
    return '{}{:03}.{}'.format('-' if a < 0 else ' ', abs(a), b)

for x in [12.0, 12.345, -12.345, -12.3456]:
    print('{:>8} -> {}'.format(x, pad(x)))

Output:

    12.0 ->  012.0
  12.345 ->  012.345
 -12.345 -> -012.345
-12.3456 -> -012.3456
Cees Timmerman
  • 17,623
  • 11
  • 91
  • 124