5

I'm trying to write a Python 2.5.4 code to write a function that takes a floating-point number x as input and returns the number of digits after the decimal point in x.

Here's my code:

def number_of_digits_post_decimal(x):
    count = 0
    residue = x -int(x)
    if residue != 0:
        multiplier = 1
        while int(multiplier * residue) != (multiplier * residue):
            count += 1
            multiplier = 10 * multiplier
            print count
            print multiplier
            print multiplier * residue
            print int(multiplier * residue)
            return count

print number_of_digits_post_decimal(3.14159)

The print statements within the while loop are only for debugging purposes.

Now when I run this code, I get the following as output.

1

10

1.4159

1

2

100

14.159

14

3

1000

141.59

141

4

10000

1415.9

1415

5

100000

14159.0

14158

6

1000000

141590.0

141589

7

10000000

1415900.0

1415899

8

100000000

14159000.0

14158999

9

1000000000

....

The final value of count as returned by this function is 17.

How to modify this code in order to achieve our desired result?

Community
  • 1
  • 1
Saaqib Mahmood
  • 141
  • 1
  • 1
  • 6
  • In the code that you posted, you always return from the loop in the first iteration. – tobias_k Feb 23 '16 at 19:08
  • 4
    If you want to count decimal digits, why don't you just convert to string and count? Your approach will probably not work due to floating point precision issues. – tobias_k Feb 23 '16 at 19:10
  • The problem here is a rounding error due to how floating points are stored. Floating points are base 2, not base 10, so there is probably a very small repeating digit in the binary representation that doesn't get cancelled out properly. The only solution I can think of would be to replace `while int(multiplier * residue) != (multiplier * residue):` with something like `while 0.0000001 < ((multiplier * residue) - int(multiplier * residue)):` to ignore trivial differences, but that is not a very good solution. – Kyle A Feb 23 '16 at 19:13
  • tobias_k has a much better solution than mine. – Kyle A Feb 23 '16 at 19:13
  • 3
    http://stackoverflow.com/questions/17837654/count-number-of-digits-after-in-floating-point-numbers – Padraic Cunningham Feb 23 '16 at 19:37
  • See the excellent answer by Keith Thompson on the question Padraic Cunningham linked to. Short answer: you can't really do this, as stated. – Mark Dickinson Feb 23 '16 at 19:51
  • 1
    Does this answer your question? [Easy way of finding decimal places](https://stackoverflow.com/questions/6189956/easy-way-of-finding-decimal-places) – eadmaster Feb 13 '20 at 08:42

4 Answers4

6

Here's a shortcut that you might like:

def num_after_point(x):
    s = str(x)
    if not '.' in s:
        return 0
    return len(s) - s.index('.') - 1
DevShark
  • 8,558
  • 9
  • 32
  • 56
  • 1
    This gives the wrong answer for `x=1e-5`, among other numbers. – Mark Dickinson Feb 24 '16 at 07:01
  • @MarkDickinson what number is this? Do you mean $10^{-5}$? – Saaqib Mahmood Feb 26 '16 at 07:10
  • @SaaqibMahmuud: Yes; that's scientific notation for `10**-5`. `num_after_point(1e-5)` will give zero. – Mark Dickinson Feb 26 '16 at 07:15
  • @MarkDickinson but you can enter this same number as 0.00001, can't you? Then DevShark's code will work, won't it? How would you like to improve his code? – Saaqib Mahmood Feb 26 '16 at 07:29
  • I added the solution to this problem – DevShark Feb 26 '16 at 08:08
  • 1
    @SaaqibMahmuud: No, the original solution doesn't work in this case. `str(0.00001)` is `1e-05`. – Mark Dickinson Feb 26 '16 at 08:21
  • @DevShark: Did you test your new code? It doesn't work at all. (It returns `6` for almost any input.) Not to mention that the OP is on Python 2.5, where the `str.format` method doesn't exist. – Mark Dickinson Feb 26 '16 at 08:22
  • I did not. You're right, it does not work, I removed it. @SaaqibMahmuud I don;t know if you need scientific notation, but if you do, this could give you an idea. You could use something like `'{:.50f}'.format(1e-10)` or `'%f' % 1e-5` and check for when you have many zeros. – DevShark Feb 26 '16 at 09:37
  • python 3.8: `n = len(sx.split('.')[1]) if '.' in (sx := str(x)) else 0` – iperov Aug 21 '23 at 05:15
3

This was interesting! So if you run the following:

x = 3.14159  
residue = x - int(x)  
print residue  

You will get the following result:

0.14158999999999988

This decimal does in fact have 17 digits. The only way that I found to override this was to avoid doing the subtraction (which is the root cause of the error, as you can see from the inaccuracy here). So this code should work as you expect:

def number_of_digits_post_decimal(x):  
    count = 0  
    residue = x -int(x)  
    if residue != 0:  
        multiplier = 1  
        while not (x*multiplier).is_integer():  
            count += 1  
            multiplier = 10 * multiplier  
        return count

This will just shift the decimal to the right until python identifies it as an integer (it will do a rightward shift exactly the number of times you want too). Your code actually worked as you intended for it to, something unintended just happened during the subtraction process. Hope this helps!

Kushan Gunasekera
  • 7,268
  • 6
  • 44
  • 58
JPope2014
  • 161
  • 1
  • 10
  • the method is_integer isn't available in Python 2.5.4. – Saaqib Mahmood Feb 26 '16 at 07:11
  • This method gave "5" for "0.7576" simply because at the 4th iteration, `multiplier = 10000` and `x * mulplier = 7576.000000000001`. This can be solved by adding `from math import floor`, a threshold `threshold=1e8` and using `while not (floor(threshold*x*multiplier)/threshold).is_integer():` – Shadi Sep 08 '17 at 07:05
  • Also, just ran into `0.0169` yielding 18 with this function, because it's showing up as `0.16899999999999998` when `multiplier=10`. (shrug) – Shadi Sep 08 '17 at 08:13
  • number_of_digits_post_decimal( 32.91647 ) returns 15 instead of 5 – Johny White Jul 20 '21 at 23:42
0
def decimal_places(f):
    exp = -1
    remainder = True
    while remainder:
        exp += 1
        a = f * 10**exp
        remainder = int(a) - a
    return(exp)
0
def precision(f):
    integer, remainder = str(f).split('.')
    return len(remainder)