2

Assume to have this number:

p=0.00001

Now, if we print this variable we get 1e-05 (i.e., that number in scientific notation). How can I print exactly 0.00001 (i.e., in decimal notation)? Ok, I know I can use format(p, '.5f'), but the fact is that I don't know in advance the number of decimal digits (e.g., I may have 0.01 or 0.0000001, etc.). Therefore, I could count the number of decimal digits and use that in the format function. In doing so, I have a related problem... is there any way to count the decimal digits of a decimal number without using Decimal?

redcrow
  • 1,743
  • 3
  • 25
  • 45
  • possible duplicate of [How do I suppress scientific notation in Python?](http://stackoverflow.com/questions/658763/how-do-i-suppress-scientific-notation-in-python) – Celeo Oct 23 '14 at 20:04
  • @Celeo: Sorry, but I don't think it's a duplicate of that. I specified I know how to format in that way, but what I'm asking is how to print the exact number of digits, so I cannot use `format` with a prefixed `.xf` like in other questions in stack. – redcrow Oct 23 '14 at 20:09
  • @PadraicCunningham: I'm not sure what the `split` is there for, but… try that with his example; since `str(p)` is `1e-05`, what are you going to get useful out of the length of any part of that? – abarnert Oct 23 '14 at 20:11
  • @PadraicCunningham: that does not work because `str(p)` is `1e-05`, so there is not any `.` – redcrow Oct 23 '14 at 20:12
  • Your problem here isn't really answerable, because it's based a misapprehension. `p` is a Python `float`, which means it's a binary fraction, not a decimal fraction, which means that asking how many decimal digits it has makes no sense. For _some_ values, of course, there is a binary fraction that fits into an IEEE double that exactly matches the decimal value you started with, but that's not true in general. – abarnert Oct 23 '14 at 20:12
  • If you haven't read [What every computer scientist should know about floating-point](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) go read it. But first, just consider this: How many decimal places does 1/3 have? – abarnert Oct 23 '14 at 20:19
  • @abarnert: we are talking about the same topic in two different parts. :) Please read my reply in the Ignacio's answer. – redcrow Oct 23 '14 at 20:24
  • `print(format(p,"."+str(p).split("e")[-1][1:]+"f")) if "e" in str(p) else print(p)` – Padraic Cunningham Oct 23 '14 at 20:31
  • @PadraicCunningham: I think the correct instruction is: `print(format(p,"."+str(p).split("e")[-1][1:]+"f")) if "e" in str(p) else str(p)` with `str(p)` instead of `print(p)` in the end... but YES, that is exactly what I was looking for. If you write this as answer I'll vote for that. – redcrow Oct 23 '14 at 20:35

4 Answers4

2

... is there any way to count the decimal digits of a float number without using Decimal?

Nope. And there isn't any way to do it using Decimal either. Floating point numbers that aren't an exact sum of powers of 1/2 don't have an exact number of decimal places. The best you can do is perform some sort of guesstimate based on the leftmost non-zero digit and some arbitrary count of digits.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • Well, you can count the decimal digits of the shortest decimal fraction that rounds to a particular binary fraction, which is often what you want. But the easiest way to do that in Python 3.x is to just format it as a string and get the length, because the algorithm is already built in… – abarnert Oct 23 '14 at 20:06
  • There's a way for my case... I may use this: `p = decimal.Decimal('0.00001')` and `abs(p.as_tuple().exponent)`, but I'd like to know if there's any way without using `Decimal`. – redcrow Oct 23 '14 at 20:15
  • @f_ficarola: That works because `Decimal('0.00001')` is starting with an exact decimal fraction and never constructing a `float` in the first place. I'm pretty sure Ignacio was saying that you can't use `Decimal` to get the number of decimal places of a `float`, because you can't. – abarnert Oct 23 '14 at 20:18
  • Yes, you're right... maybe I didn't explain my problem in the best way. I don't care about other floats. I have a set of my own floats (e.g., `0.000001`, `0.00001`, `0.00000001`, etc.) and I want them to be printed as they are processed. For instance, in a `for` loop I get `0.000001` in the first iteration, then `0.00000001` and so on, how can I print them? – redcrow Oct 23 '14 at 20:22
  • To avoid misunderstandings, maybe I should say "a set of decimal numbers", not floats. – redcrow Oct 23 '14 at 20:31
  • @f_ficarola: I still think you're misunderstanding. If you have the Python value `0.000001`, that is not a decimal number, it's a float. If you have `'0.000001'` or `Decimal('0.000001')`, that's a different story; just don't convert to `float` and you don't have a problem. But if you've got floats, you don't have decimal numbers. – abarnert Oct 23 '14 at 21:40
1

In general you can't count the number of decimal digits, but you can compute the minimum precision and use it with f format specification in str.format or equivalent function:

from math import ceil, log10
p = 0.00001
precision = int(ceil(abs(log10(abs(p))))) if p != 0 else 1
'{:.{}f}'.format(p, precision)

You'll need to increase precision if you want to display more than one significant digit. Also you might need a slightly different logic for numbers > 1.

vitaut
  • 49,672
  • 25
  • 199
  • 336
0

As of Python 3.6.x and above versions, f-strings are a great new way to format strings. Not only are they more readable, more concise, and less prone to error than other ways of string formatting.
You can read more about f-strings on https://datagy.io/python-f-strings/

Coming to your question,

number = 0.00001    # 1e-5
print(f"{number:f}")

output:

0.000010

This also works directly e.g.

number = 1e-5
print(f"{number:f}")

output:

0.000010

But this method can show you digits up to 1e-06
for example

number = 0.000001    # 1e-6
print(f"{number:f}")

output:

0.000001

which works but if you go further

number = 0.0000001    # 1e-7
print(f"{number:f}")

output:

0.000000

So be careful using this.

DharmanBot
  • 1,066
  • 2
  • 6
  • 10
-1
p = 0.0000000000000000000001

s = str(p)

print(format(p, "." + s.split("e")[-1][1:]+"f")) if "e" in s else print(p)
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • 1
    This prints exactly one significant digit for `p < 1e-5`, and a whole mess of useless digits for `p > 1e5`. Try it with, say, `4.56e-5`. It just happens to work for the three examples—but if you only want to work for those 3 examples there are easier and less hackier ways to do that. – abarnert Oct 23 '14 at 21:39
  • 1
    No it isn't. Did you miss the difference between `-` and `+`? For `1e-5`he wants `0.00001`; this works. For 4.56e-5 he presumably wants `0.0000465`, but you're going to print `0.00005`, so this doesn't work. In fact, there's a very limited set of values for which this _does_ work. If he only cares about the three specific examples in the question, he might as well just map those examples to whatever string he wants. If he wants something that actually does what he asks for more than those examples, this doesn't do it. – abarnert Oct 23 '14 at 22:46
  • 1
    You seriously think those 3 examples are the only things he cares about? – abarnert Oct 23 '14 at 23:59
  • No, it won't work for any value from 0.1 to 1e-140. It won't work for `0.011`, for example. It will "count the decimal digits" as 2, instead of 3, because it's not actually counting the decimal digits, it's just counting the leading zeroes. – abarnert Oct 24 '14 at 00:14
  • OK, sorry, yes, because that one isn't small enough to use exponential notation, so it's not even running your code. You could try any of the examples I gave earlier—like 0.0000465, which prints `0.00005`. The explanation is the same: your code is counting leading zeroes, not counting decimal digits. – abarnert Oct 24 '14 at 00:28