How to convert a floating-point number to a fixed-width string? - python

I tried to find this question answered, but I haven't found anything related.
I have a variable that can be in a format like 50000.45 or in a format like 0.01.
I need to write this variable in a label that is 4 digits wide.
What is the best way to fit the label showing only the most significant digits?
To better explain, I would like to have for example:
for 50000.45: 50000
for 4786.847: 4786
for 354.5342: 354.5
for 11.43566: 11.43
and for 0.014556: 0.0145
Possibly without having to do:
if ... < variable < ...:
round(variable,xx)
for all cases.

In order to convert a number into a set number of digits, you can convert the number into only decimals (aka 0 <= n <= 1), then remove the last characters. You can do it like that:
from math import log10
number = 123.456789
n_digits = 4
log = int(log10(number) + 1)
number /= 10**log # convert the number into only decimals
number = int(number*10**n_digits)/10**n_digits # keep the n first digits
number *= 10**log # multiply the number back
Or a more compact form:
from math import log10
number = 123.456789
n_digits= 4
log = int(log10(number) + 1) - n_digits
number = int(number/10**log)*10**log
[Edit] You should use Python round() function in a simpler way:
number = round(number, n_digits-int(log10(number))-1)

Related

How do I force a python Decimal to have atleast two decimals?

I would need to format a python Decimal object to have atleast two decimals, but no more than 5. Is there a reliable way to do this?
Examples:
1.6 --> 1.60
1.678 --> 1.678
1.98765 --> 1.98765
If there are more than two decimals, it is vital that it does not get truncated to only two decimals.
It looks to me like there are two parts to this question - one, determining the correct number of digits and two, quantizing the values to that number of digits.
To do the first, I would get the current exponent using the as_tuple() method. Unless I'm overlooking something simpler.
>>> import decimal
>>> d = decimal.Decimal("1.678")
>>> d.as_tuple().exponent
-3
>>> d2 = decimal.Decimal("1.6")
>>> d2.as_tuple().exponent
-1
So from that you can compute the desired exponent:
MAX_EXPONENT = -2
MIN_EXPONENT = -5
def desired_exponent(d):
current_exponent = d.as_tuple().exponent
return min(MAX_EXPONENT, max(MIN_EXPONENT, current_exponent))
The second is answered by the accepted answer on the marked duplicate - use the quantize() method. You'll need to construct a Decimal value with the desired exponent you can provide as the argument to quantize(). There are multiple ways to do that, but two simple ones are exponentiating decimal.Decimal("10") or using the tuple constructor for decimal.Decimal().
>>> quant_arg = decimal.Decimal("10") ** -2
>>> decimal.Decimal("1.6").quantize(quant_arg)
Decimal('1.60')
Or:
>>> quant_arg = decimal.Decimal((0, (), -2))
>>> decimal.Decimal("1.6").quantize(quant_arg)
Decimal('1.60')
I used -2 as a literal there, you'd want to use the calculated value of desired_exponent.
There are multiple ways to organize this code, I think the parts that are not obvious are a) accessing the current exponent of a decimal value and b) some of the ways of constructing an arg for quantize(). And this is all assuming you need the actual decimal objects, and aren't just outputting them - if this is a question just about output formatting re-quantizing is probably overkill.
Here is the code I use now:
def unitAmount(value):
"""Format a Decimal to match -?[0-9]{1,15}(,[0-9]{2,5})?
Minimum two decimals, max 5.
"""
decimals = value.as_tuple().exponent
if decimals == -1: # For values like 1.6 --> 1.60
value = value.quantize(Decimal('1.00'))
elif decimals < -5: # For values like 1.1234567.. --> 1.12345
value = value.quantize(Decimal('1.00000'))
return value

How do I make a float only show a certain amount of decimals

I have a float that has 16 decimal places, but I want it to be capped at 6, and if I ever get a float that has less than 6 decimals, I want it to add 0s until it has 6.
i.e.:
1.95384240549 = 1.953842
3.12 = 3.120000
I'm trying to generate a value based on a certain amount of demand an object has. Thanks!
To round to a certain amount of decimals you can use round()
Example:
round(1.95384240549,6) > 1.953842
And for more 0's after the decimal place you can use format():
format(3.12, '.6f') > '3.120000'
Note this is of type string
Read more here:
Rounding syntax
Format syntax
A bit more complex than the previous answer, but more consistent.
import math
def truncate(number, digits) -> float:
places = len(str(number)[str(number).find("."):])
if places > 6:
stepper = 10.0 ** digits
return math.trunc(stepper * number) / stepper
else:
return str(number) + "0"*(6 - places)
Examples: truncate(3.12 , 6) returns 3.120000 and truncate(1.95384240549, 6) returns 1.953842

Print floating point number with a set number of significant digits (non-scientific)

I want to print floating point numbers with a set number of significant (i.e. non-zero) digits, but avoid the scientific notation (e).
So, for example, the number 0.000000002343245345 should be printed as 0.000000002343 (and not 2.343e-09)
I know how to define number of significant digits with an e notation:
>>>print('{:.3e}'.format(0.000000002343245345))
2.343e-09
And how to print a set number of decimal places without e-notation:
>>>print('{:.12f}'.format(0.000000002343245345))
0.000000002343
but not how to combine the two.
is there any simple way of doing so?
Here is some code that usually does what you want.
x = 0.000000002343245345
n = 4
from math import log10, floor
print('{:.{}f}'.format(x, n - floor(log10(x)) - 1))
Note that, due to the lack of exactness in floating-point arithmetic, that this may occasionally give a wrong answer. For example, the floor(log10()) may be one off from what is expected at or very near negative powers of 10 such as 0.1, 0.01, 0.001, and so on. My code seems to work well with those values but that is not guaranteed.
Also, there is no reasonable answer for some combinations of x and n. For example, what do you expect to result from x = 200000 and n = 4? There is no good answer, and my code gives the error
ValueError: Format specifier missing precision
You have to calculate the number of digits yourself. For four significant digits, this would be
number = 0.000000002343245345
digits = 4 - int(math.ceil(math.log10(number)))
print("{:.{}f}".format(number, digits))
# 0.000000002343

Python: Setting up a binary-number string converter, then indexing the result

I have a bit of an challenge before me.
Currently I'm trying to accomplish this process:
Feed a decimal, or any number really, into a binary converter
Now that we possess a binary string, we must measure the length of the string. (as in, numstr="10001010" - I want the return to count the characters and return "8")
Finally, I need to extract a section of said string, if I want to cut out the first half of the string "10001010" and save both halves, I want the return to read "1000" and "1010"
Current Progess:
newint=input("Enter a number:")
newint2= int(newint)
binStr=""
while newint2>0:
binStr= str(newint2%2) + binStr
newint2= newint2//2
print (binStr)
newint = input("Enter a binary number:")
temp=newint
power = 0
number = 0
while len(temp) > 0:
bit=int(temp[-1])
number = number + bit * 2 ** power
power+=1
temp = temp[:-1]
print(number)
//This works for integer values, how do I get it to also work for decimal values, where the integer is either there or 0 (35.45 or 0.4595)?
This is where I'm lost, I'm not sure what the best way to attempt this next step would be.
Once I convert my decimal or integer into binary representation, how can I cut my string by varying lengths? Let's say my binary representation is 100 characters, and I want to cut out lengths that are 10% the total length, so I get 10 blocks of 10 characters, or blocks that are 20% total length so I have 5 blocks of 20 characters.
Any advice is appreciated, I'm a super novice and this has been a steep challenge for me.
Strings can be divided up through slice notation.
a='101010101010'
>>>a[0]
'1'
>>>a[0:5]
'10101'
>>>a[0:int(len(a)/2)]
'101010'
That's something you should read up on, if you're getting into Python.
Here is my suggestion, based on answer from What's the best way to split a string into fixed length chunks and work with them in Python? :
def chunkstring(string, percent):
length = int(len(string) * percent / 100)
return ([string[0+i:length+i] for i in range(0, len(string), length)])
# Define a string with 100 characters
a = '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'
# Split this string by percentage of total length
print(chunkstring(a, 10)) # 10 %
print(chunkstring(a, 20)) # 20 %

Generate a random number of len(10) containing digits only 0-1s

The question is self-explanatory.
I've tried this.
import random
number = "".join([str(random.randint(0,1)) for i in xrange(0,10)])
print number
Is there any in-built functionality for the same?
Either use:
''.join(random.choice('01') for _ in xrange(10))
Which avoids the int->str, or otherwise use randrange (to exclude a full bit set) with a range that's 2**10, then format as a binary string.
format(random.randrange(2**10), '010b')
Also, to avoid overflow, you can use getrandbits and specify 10 as the amount, eg:
format(random.getrandbits(10), '010b')
Choose a random int in the range 0 ti 1023 inclusive and format it in base 2 with a minimum width of 10 with leading 0s filled in.
format(random.randint(0,1023), '010b')
I’m going to throw in another solution that creates an actual (decimal) number containing only ones and zeros:
>>> import random, functools
>>> functools.reduce(lambda x, i: 10 * x + random.randrange(2), range(10), 0)
1011001010
OR, to skip the decimal-binary transition :
from random import randint
def repeat(n):
if(n<=0):
return ''
n -= 1
return str(randint(0,1))+repeat(n)
and at the end just call repeat(10) or whatever number of bits you want.

Categories