6

How would I do the following:

>>> num_decimal_places('3.2220')
3 # exclude zero-padding

>>> num_decimal_places('3.1')
1

>>> num_decimal_places('4')
0

I was thinking of doing:

len((str(number) if '.' in str(number) else str(number) + '.').rstrip('0').split('.')[-1])

Is there another, simpler way to do this?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
David542
  • 104,438
  • 178
  • 489
  • 842

9 Answers9

4

You can use a regex to parse value, capture the decimal digits and count the length of the match, if any:

import re

def num_decimal_places(value):
    m = re.match(r"^[0-9]*\.([1-9]([0-9]*[1-9])?)0*$", value)
    return len(m.group(1)) if m is not None else 0

this is a bit less "raw" than splitting the string with multiple if else, not sure if simpler or more readable, though.

Stefano Sanfilippo
  • 32,265
  • 7
  • 79
  • 80
3

The best and the most Pythonic way to achieve this is:

import decimal
x = '56.001000'
x = x.rstrip('0')  # returns '56.001'
x = decimal.Decimal(x)  # returns Decimal('0.001')
x = x.as_tuple().exponent  # returns -3
x = abs(x)  #returns 3

Above code can be written in simpler way as:

>>> x = '56.001000'
>>> abs(decimal.Decimal(x.rstrip('0')).as_tuple().exponent)
3

Below is the list of used functions for more reference:

  1. str.rstrip(): Return a copy of the string with trailing characters removed.
  2. decimal.Decimal(): Construct a new Decimal object based from value.
  3. x.as_tuple(): Returns a namedtuple of the format: DecimalTuple(sign=0, digits=(1,), exponent=-3)
  4. abs(): Return the absolute value of a number.
Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
  • Thanks, I didnt know about the Decimal lib. I actually think this is more pythonic than the regex solution – 3pitt Nov 19 '20 at 02:47
  • 1
    Please use `.normalize()` on the `Decimal` instance instead of string operations. Your code will fail when the string is e.g. `1e-10`. – Feuermurmel Oct 12 '22 at 12:49
2

You dont need regex, you can convert to float and convert back to string! this automatically will remove the zeroes :

>>> def convertor(s):
...       try :
...          int(s.rstrip('0').rstrip('.'))
...          return 0
...       except: 
...          return len(str(float(s)).split('.')[-1])
... 
>>> convertor('4.0230')
3
>>> convertor('4.0')
0
>>> convertor('4')
0
Mazdak
  • 105,000
  • 18
  • 159
  • 188
1

you could also just try something like:

try:
    return len(str(num).split('.')[1].rstrip('0'))
except
    return 0
David542
  • 104,438
  • 178
  • 489
  • 842
acushner
  • 9,595
  • 1
  • 34
  • 34
1

By string process:

  1. Check . is present in number string or not.
  2. If Not present then return 0.
  3. If present the split number string by . and get second item from the split result.
  4. Remove 0 from the right side.
  5. Return len of item.

code:

>>> def dCount(no_str):
...    if "." in no_str:
...         return len(no_str.split(".")[1].rstrip("0"))
...    else:
...         return 0
... 
>>> dCount("2")
0
>>> dCount("2.002")
3
>>> dCount("2.1230")
3
>>> dCount("2.01230")
4
>>> 
Vivek Sable
  • 9,938
  • 3
  • 40
  • 56
0
import re

def f(s):
    ls = s.split('.', 1)
    if len(ls) == 2 and re.match(r'\d*$', ls[1]):
        return len(ls[1].rstrip('0'))
    return 0

assert f('12') == 0
assert f('12.') == 0
assert f('12.1') == 1
assert f('12.100006') == 6
assert f('12.1.3') == 0
assert f('12.1abc') == 0
assert f('12.100000') == 1
ferhatelmas
  • 3,818
  • 1
  • 21
  • 25
0

The Decimal type is perfect for this, you can implement num_decimal_places() as follows:

from decimal import Decimal

def num_decimal_places(value: str):
    return -Decimal(value).normalize().as_tuple().exponent

It works as follows: Decimal(value) parses the string, including exponent notation, then .normalize() strips any trailing zeros from the internal representation. .as_tuple().exponent contains the number of decimal places the internally stored integer is shifted to the left, so negative numbers specify the number of places to the right of the decimal.

Feuermurmel
  • 9,490
  • 10
  • 60
  • 90
0

Use the partition method of strings to get the decimal portion - this returns a 3-tuple regardless of whether the string actually contains a decimal point or any digits after it. Then simply remove trailing zeros from the decimal portion and count the remaining digits. Thus:

def needed_precision(number_string):
    whole, period, decimal = number_string.partition('.')
    return len(decimal.rstrip('0'))

This can of course be inlined:

len(number_string.partition('.')[2].rstrip('0'))

Which is less readable, but quite short and straightforward.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
-1

You could try using the Decimal function in python:

abs(Decimal(string_value).as_tuple().exponent)

as explained in Easy way of finding decimal places

Community
  • 1
  • 1
Pedro Braz
  • 2,261
  • 3
  • 25
  • 48