Given a string representing a real number in decimal, how can I count the number of needed (non-zero) decimal places?

Question:

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?

Asked By: David542

||

Answers:

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

Answered By: Pedro Braz

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.

Answered By: Stefano Sanfilippo

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
Answered By: Mazdak

you could also just try something like:

try:
    return len(str(num).split('.')[1].rstrip('0'))
except
    return 0
Answered By: acushner

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
>>> 
Answered By: Vivek Sable
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
Answered By: ferhatelmas

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.
Answered By: Moinuddin Quadri

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.

Answered By: Feuermurmel

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.

Answered By: Karl Knechtel
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.