a = 1
for x in range(5):
a += 0.1
print(a)
This is the result:
1.1
1.2000000000000002
1.3000000000000003
1.4000000000000004
1.5000000000000004
How can I fix this? Is the round() function the only way? Can I set the precision of a variable before setting its value?
can i set the precision of a variable before setting the value?
Use the decimal module which, unlike float(), offers arbitrary precision and can represent decimal numbers exactly:
>>> from decimal import Decimal, getcontext
>>>
>>> getcontext().prec = 5
>>>
>>> a = Decimal(1)
>>>
>>> for x in range(5):
... a += Decimal(0.1)
... print(a)
...
1.1000
1.2000
1.3000
1.4000
1.5000
You could format your output like this;
a=1
for x in range(5):
a += 0.1
print("{:.9f}".format(a) )
Assuming that your problem is only displaying the number, #Jaco 's answer does the job. However if you're concern about using that variable and potentially make comparisons or assigning to dictionary keys, I'd say you have to stick to round(). For example this wouldn't work:
a = 1
for x in range(5):
a += 0.1
print('%.1f' % a)
if a == 1.3:
break
1.1
1.2
1.3
1.4
1.5
You'd have to do:
a = 1
for x in range(5):
a += 0.1
print('%.1f' % a)
if round(a, 1) == 1.3:
break
1.1
1.2
1.3
Formatted output has been duly suggested by #Jaco. However, if you want control of precision in your variable beyond pure output, you might want to look at the decimal module.
from decimal import Decimal
a = 1
for x in range(3):
a += Decimal('0.10') # use string, not float as argument
# a += Decimal('0.1000')
print(a) # a is now a Decimal, not a float
> 1.10 # 1.1000
> 1.20 # 1.2000
> 1.30 # 1.3000
Related
I have a column (version number) with more than 200k occurences as float for instance 1.2, 0.2 ...
I need to sum both sides of the floating number into a new column (total version), so that it gives me in the example 3, 2. Just integer numbers
Any advice?
Here is a solution that should be very easy to understand. I can make a oneline also you want to have that.
mylist = [1.3, 2.6, 3.1]
number = 0
fractions = 0
for a in mylist:
(a,b)=str(a).split('.')
number = number + int(a)
fractions = fractions + int(b)
print ("Number: " + str(number))
print ("Fractions: " + str(fractions))
This gives:
Number: 6
Fractions: 10
Do not use str(x).split('.') !
The one comment and the two other answers are currently suggesting to get the integer and fractional parts of a number x using
i,f = (int(s) for s in str(x).split('.'))
While this does give a result, I believe it is a bad idea.
The problem is, if you expect a meaningful result, you need to specify the precision of the fractional part explicitly. "1.20" and "1.2" are two string representations of the same number, but 20 and 2 are two very different integers. In addition, floating-point numbers are subject to precision errors, and you could easily find yourself with a number like "1.19999999999999999999999", which is only a small rounding error away from "1.2", but results in a completely different result with this str(x).split('.') approach.
One way to avoid this chaotic behaviour is to set a precision, ie, a number of decimal places, and stick to it. For instance, when dealing with monetary values, we're used to talk about cents; although 1.5€ and 1.50€ are technically both valid, you'll always hear people say "one euro fifty" and never "one euro five". If you hear someone say "one euro oh five", it actually means 1.05€. We always add exactly two decimal places.
With this approach, there is no chaotic behaviour of 1.2 becoming (1,2) or (1,20) or (1,1999999999). If you fixed the number of decimal places to 2, then 1.2 will always map to (1,20) and that's that.
A more standard way
Here are two standard ways of getting the integer and fractional parts of a number in python:
x = 1.20
# method 1
i = int(x)
f = x - i
# i = 1 and f = 0.2; i is an int and f a float
# method 2
import math
f, i = math.modf(x)
# i = 1.0 and f = 0.2; i and f are both floats
(EDIT: There is also a third method, pandas' divmod function. See user2314737's answer.)
Once you've done that, you can turn the fractional part f into an integer by multiplying it with the chosen power of 10 and converting it to an integer:
f = int(f * 100)
# f = 20
Finally you can apply this method to a whole list:
data = [13.0, 14.20, 12.299, 4.414]
def intfrac_pair(x, decimal_places):
i = int(x)
f = int((10**decimal_places) * (x - i))
return (i, f)
data_as_pairs = [intfrac_pair(x, 2) for x in data]
# = [(13, 0), (14, 20), (12, 30), (4, 41)]
sum_of_integer_parts = sum(i for i,f in data_as_pairs) # = 43
sum_of_fractional_parts = sum(f for i,f in data_as_pairs) # = 91
The following should work:
df['total_number']=[sum([int(i) for i in str(k).split('.')]) for k in df.version_number]
You can use divmod on the column
df = pd.DataFrame([1.2, 2.3, 3.4, 4.5, 0.1])
df
# 0
# 0 1.2
# 1 2.3
# 2 3.4
# 3 4.5
# 4 0.1
df['i'], df['d'] = df[0].divmod(1)
df
# Out:
# 0 i d
# 0 1.2 1.0 0.2
# 1 2.3 2.0 0.3
# 2 3.4 3.0 0.4
# 3 4.5 4.0 0.5
# 4 0.1 0.0 0.1
To sum row-wise as integers (a precision is needed, here I use p=1 assuming the original floats contain only one decimal digit) :
p = 1
df['s'] = (df['i']+10**p*df['d'].round(decimals=p)).astype(np.int)
df
# Out:
# 0 i d s
# 0 1.2 1.0 0.2 3
# 1 2.3 2.0 0.3 5
# 2 3.4 3.0 0.4 7
# 3 4.5 4.0 0.5 9
# 4 0.1 0.0 0.1 1
Sum by columns:
df.sum()
# Out:
# 0 11.5
# i 10.0
# d 1.5
Note: this will only work for positive integers as for instance divmod(-3.4, 1) returns (-4.0, 0.6).
Thank you all guys. I finally managed in a quite stupid, but effictive way. Before splitting, I transformed it to a string:
Allfiles['Version'] = Allfiles['Version'].round(3).astype(str)
Note that I rounded to 3 digits because a number like 2.111 was transformed to 2.11099999999999999999
Then I just did the split, creating a new column for minor versions (and having the major in the original colum
Allfiles[['Version', 'minor']] = Allfiles['Version'].str.split('.', expand=True)
Then I converted again both files into integers and sum both in the first column.
Allfiles['Version'] = Allfiles['Version']+Allfiles['minor']
(My dataframe name is Allfiles and the column version, as you can imagine.
is there any way to output the difference between two float numbers as an integer
below is three examples of the float values provided for script, my goal is to output the difference between these values as an integer , in the first example i should get 2 where num_two - num_one equals 0.000002 but i don't want the zeros as they don't matter i can do it with string format but i have no way of telling how big the number is or how many zeros it has
## example 1
num_one = 0.000012
num_two = 0.000014
## example 2
num_0ne = 0.0123
num_tw0 = 0.013
## example 3
num_1 = 23.32
num_2 = 23.234
print (float(num_2) - float(num_1))
## this should output 86 as an integer
Beware of floats (see https://en.wikipedia.org/wiki/IEEE_754):
>>> 23.32 - 23.234
0.08599999999999852
You need exact precision. Use the decimal module:
>>> from decimal import Decimal
>>> n1 = Decimal("23.32")
>>> n2 = Decimal("23.234")
>>> n1, n2
(Decimal('23.32'), Decimal('23.234'))
>>> d = abs(n1-n2)
>>> d
Decimal('0.086')
Now, just shift the decimal point right (that is * 10) until there is no fractional part left (d % 1 == 0):
>>> while d % 1:
... d *= 10
(Don't be afraid, the loop will stop because you can't have more decimal levels than decimal.getcontext().prec at the beginning and the decimal level decrease on each iteration).
You get the expected result:
>>> d
Decimal('86.000')
>>> int(d)
86
How can I do the following rounding in python:
Round to the nearest 0.05 decimal
7,97 -> 7,95
6,72 -> 6,70
31,06 -> 31,05
36,04 -> 36,05
5,25 -> 5,25
Hope it makes sense.
def round_to(n, precision):
correction = 0.5 if n >= 0 else -0.5
return int( n/precision+correction ) * precision
def round_to_05(n):
return round_to(n, 0.05)
def round05(number):
return (round(number * 20) / 20)
Or more generically:
def round_to_value(number,roundto):
return (round(number / roundto) * roundto)
The only problem is because you're using floats you won't get exactly the answers you want:
>>> round_to_value(36.04,0.05)
36.050000000000004
There we go.
round(VALUE*2.0, 1) / 2.0
regards
Here's a one liner
def roundto(number, multiple):
return number+multiple/2 - ((number+multiple/2) % multiple)
Using lambda function:
>>> nearest_half = lambda x: round(x * 2) / 2
>>> nearest_half(5.2)
5.0
>>> nearest_half(5.25)
5.5
>>> nearest_half(5.26)
5.5
>>> nearest_half(5.5)
5.5
>>> nearest_half(5.75)
6.0
To round it to exactly how you want to:
>>> def foo(x, base=0.05):
... return round(base*round(x/base), 2)
>>> foo(5.75)
5.75
>>> foo(5.775)
5.8
>>> foo(5.77)
5.75
>>> foo(7.97)
7.95
>>> foo(6.72)
6.7
>>> foo(31.06)
31.05
>>> foo(36.04)
36.05
>>> foo(5.25)
5.25
I faced the same problem and as I didn't find the ultimate solution to this, here's mine.
Firs of all the main part(which was answered before):
def round_to_precision(x, precision):
# This correction required due to float errors and aims to avoid cases like:
# 100.00501 / 0.00001 = 10000500.999999998
# It has a downside as well - it may lead to vanishing the difference for case like
# price = 100.5 - (correction - correction/10), precision = 1 => 101 not 100
# 5 decimals below desired precision looks safe enough to ignore
correction = 1e-5 if x > 0 else -1e-5
result = round(x / precision + correction) * precision
return round(result, find_first_meaningful_decimal(precision))
The only tricky part here was that find_first_meaningful_decimal, which I've implemented like this:
def find_first_meaningful_decimal(x):
candidate = 0
MAX_DECIMAL = 10
EPSILON = 1 / 10 ** MAX_DECIMAL
while round(x, candidate) < EPSILON:
candidate +=1
if candidate > MAX_DECIMAL:
raise Exception('Number is too small: {}'.format(x))
if int(x * 10 ** (candidate + 1)) == 5:
candidate += 1
return candidate
print(round_to_precision(129.950002, 0.0005))
print(round_to_precision(-129.95005, 0.0001))
129.9505
-129.9501
import numpy as np
for Roundup
df['a']=(df["a"]*2).apply(np.ceil)/2
for Round
df['a']=(df["a"]*2).apply(np.floor)/2
This is working with columns for roundup 0.5 using numpy...
An extension of the accepted answer.
def round_to(n, precision):
correction = precision if n >= 0 else -precision
return round(int(n/precision+correction)*precision, len(str(precision).split('.')[1]))
test_cases = [101.001, 101.002, 101.003, 101.004, 101.005, 101.006, 101.007, 101.008, 101.009]
[round_to(-x, 0.003) for x in test_cases]
[-101.001, -101.001, -101.001, -101.004, -101.004, -101.004, -101.007, -101.007, -101.007]
How can I take a float variable, and control how far out the float goes without round()? For example.
w = float(1.678)
I want to take x and make the following variables out of it.
x = 1.67
y = 1.6
z = 1
If I use the respective round methods:
x = round(w, 2) # With round I get 1.68
y = round(y, 1) # With round I get 1.7
z = round(z, 0) # With round I get 2.0
It's going to round and alter the numbers to the point where there no use to me. I understand this is the point of round and its working properly. How would I go about getting the information that I need in the x,y,z variables and still be able to use them in other equations in a float format?
You can do:
def truncate(f, n):
return math.floor(f * 10 ** n) / 10 ** n
testing:
>>> f=1.923328437452
>>> [truncate(f, n) for n in range(7)]
[1.0, 1.9, 1.92, 1.923, 1.9233, 1.92332, 1.923328]
A super simple solution is to use strings
x = float (str (w)[:-1])
y = float (str (w)[:-2])
z = float (str (w)[:-3])
Any of the floating point library solutions would require you dodge some rounding, and using floor/powers of 10 to pick out the decimals can get a little hairy by comparison to the above.
Integers are faster to manipulate than floats/doubles which are faster than strings. In this case, I tried to get time with both approach :
timeit.timeit(stmt = "float(str(math.pi)[:12])", setup = "import math", number = 1000000)
~1.1929605630000424
for :
timeit.timeit(stmt = "math.floor(math.pi * 10 ** 10) / 10 ** 10", setup = "import math", number = 1000000)
~0.3455968870000561
So it's safe to use math.floor rather than string operation on it.
If you just need to control the precision in format
pi = 3.14159265
format(pi, '.3f') #print 3.142 # 3 precision after the decimal point
format(pi, '.1f') #print 3.1
format(pi, '.10f') #print 3.1415926500, more precision than the original
If you need to control the precision in floating point arithmetic
import decimal
decimal.getcontext().prec=4 #4 precision in total
pi = decimal.Decimal(3.14159265)
pi**2 #print Decimal('9.870') whereas '3.142 squared' would be off
--edit--
Without "rounding", thus truncating the number
import decimal
from decimal import ROUND_DOWN
decimal.getcontext().prec=4
pi*1 #print Decimal('3.142')
decimal.getcontext().rounding = ROUND_DOWN
pi*1 #print Decimal('3.141')
I think the easiest answer is :
from math import trunc
w = 1.678
x = trunc(w * 100) / 100
y = trunc(w * 10) / 10
z = trunc(w)
also this:
>>> f = 1.678
>>> n = 2
>>> int(f * 10 ** n) / 10 ** n
1.67
Easiest way to get integer:
series_col.round(2).apply(lambda x: float(str(x).split(".",1)[0]))
I have a long list of Decimals and that I have to adjust by factors of 10, 100, 1000,..... 1000000 depending on certain conditions. When I multiply them there is sometimes a useless trailing zero (though not always) that I want to get rid of. For example...
from decimal import Decimal
# outputs 25.0, PROBLEM! I would like it to output 25
print Decimal('2.5') * 10
# outputs 2567.8000, PROBLEM! I would like it to output 2567.8
print Decimal('2.5678') * 1000
Is there a function that tells the decimal object to drop these insignificant zeros? The only way I can think of doing this is to convert to a string and replace them using regular expressions.
Should probably mention that I am using python 2.6.5
EDIT
senderle's fine answer made me realize that I occasionally get a number like 250.0 which when normalized produces 2.5E+2. I guess in these cases I could try to sort them out and convert to a int
You can use the normalize method to remove extra precision.
>>> print decimal.Decimal('5.500')
5.500
>>> print decimal.Decimal('5.500').normalize()
5.5
To avoid stripping zeros to the left of the decimal point, you could do this:
def normalize_fraction(d):
normalized = d.normalize()
sign, digits, exponent = normalized.as_tuple()
if exponent > 0:
return decimal.Decimal((sign, digits + (0,) * exponent, 0))
else:
return normalized
Or more compactly, using quantize as suggested by user7116:
def normalize_fraction(d):
normalized = d.normalize()
sign, digit, exponent = normalized.as_tuple()
return normalized if exponent <= 0 else normalized.quantize(1)
You could also use to_integral() as shown here but I think using as_tuple this way is more self-documenting.
I tested these both against a few cases; please leave a comment if you find something that doesn't work.
>>> normalize_fraction(decimal.Decimal('55.5'))
Decimal('55.5')
>>> normalize_fraction(decimal.Decimal('55.500'))
Decimal('55.5')
>>> normalize_fraction(decimal.Decimal('55500'))
Decimal('55500')
>>> normalize_fraction(decimal.Decimal('555E2'))
Decimal('55500')
There's probably a better way of doing this, but you could use .rstrip('0').rstrip('.') to achieve the result that you want.
Using your numbers as an example:
>>> s = str(Decimal('2.5') * 10)
>>> print s.rstrip('0').rstrip('.') if '.' in s else s
25
>>> s = str(Decimal('2.5678') * 1000)
>>> print s.rstrip('0').rstrip('.') if '.' in s else s
2567.8
And here's the fix for the problem that #gerrit pointed out in the comments:
>>> s = str(Decimal('1500'))
>>> print s.rstrip('0').rstrip('.') if '.' in s else s
1500
Answer from the Decimal FAQ in the documentation:
>>> def remove_exponent(d):
... return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
>>> remove_exponent(Decimal('5.00'))
Decimal('5')
>>> remove_exponent(Decimal('5.500'))
Decimal('5.5')
>>> remove_exponent(Decimal('5E+3'))
Decimal('5000')
Answer is mentioned in FAQ (https://docs.python.org/2/library/decimal.html#decimal-faq) but does not explain things.
To drop trailing zeros for fraction part you should use normalize:
>>> Decimal('100.2000').normalize()
Decimal('100.2')
>> Decimal('0.2000').normalize()
Decimal('0.2')
But this works different for numbers with leading zeros in sharp part:
>>> Decimal('100.0000').normalize()
Decimal('1E+2')
In this case we should use `to_integral':
>>> Decimal('100.000').to_integral()
Decimal('100')
So we could check if there's a fraction part:
>>> Decimal('100.2000') == Decimal('100.2000').to_integral()
False
>>> Decimal('100.0000') == Decimal('100.0000').to_integral()
True
And use appropriate method then:
def remove_exponent(num):
return num.to_integral() if num == num.to_integral() else num.normalize()
Try it:
>>> remove_exponent(Decimal('100.2000'))
Decimal('100.2')
>>> remove_exponent(Decimal('100.0000'))
Decimal('100')
>>> remove_exponent(Decimal('0.2000'))
Decimal('0.2')
Now we're done.
Use the format specifier %g. It seems remove to trailing zeros.
>>> "%g" % (Decimal('2.5') * 10)
'25'
>>> "%g" % (Decimal('2.5678') * 1000)
'2567.8'
It also works without the Decimal function
>>> "%g" % (2.5 * 10)
'25'
>>> "%g" % (2.5678 * 1000)
'2567.8'
I ended up doing this:
import decimal
def dropzeros(number):
mynum = decimal.Decimal(number).normalize()
# e.g 22000 --> Decimal('2.2E+4')
return mynum.__trunc__() if not mynum % 1 else float(mynum)
print dropzeros(22000.000)
22000
print dropzeros(2567.8000)
2567.8
note: casting the return value as a string will limit you to 12 significant digits
Slightly modified version of A-IV's answer
NOTE that Decimal('0.99999999999999999999999999995').normalize() will round to Decimal('1')
def trailing(s: str, char="0"):
return len(s) - len(s.rstrip(char))
def decimal_to_str(value: decimal.Decimal):
"""Convert decimal to str
* Uses exponential notation when there are more than 4 trailing zeros
* Handles decimal.InvalidOperation
"""
# to_integral_value() removes decimals
if value == value.to_integral_value():
try:
value = value.quantize(decimal.Decimal(1))
except decimal.InvalidOperation:
pass
uncast = str(value)
# use exponential notation if there are more that 4 zeros
return str(value.normalize()) if trailing(uncast) > 4 else uncast
else:
# normalize values with decimal places
return str(value.normalize())
# or str(value).rstrip('0') if rounding edgecases are a concern
You could use :g to achieve this:
'{:g}'.format(3.140)
gives
'3.14'
This should work:
'{:f}'.format(decimal.Decimal('2.5') * 10).rstrip('0').rstrip('.')
Just to show a different possibility, I used to_tuple() to achieve the same result.
def my_normalize(dec):
"""
>>> my_normalize(Decimal("12.500"))
Decimal('12.5')
>>> my_normalize(Decimal("-0.12500"))
Decimal('-0.125')
>>> my_normalize(Decimal("0.125"))
Decimal('0.125')
>>> my_normalize(Decimal("0.00125"))
Decimal('0.00125')
>>> my_normalize(Decimal("125.00"))
Decimal('125')
>>> my_normalize(Decimal("12500"))
Decimal('12500')
>>> my_normalize(Decimal("0.000"))
Decimal('0')
"""
if dec is None:
return None
sign, digs, exp = dec.as_tuple()
for i in list(reversed(digs)):
if exp >= 0 or i != 0:
break
exp += 1
digs = digs[:-1]
if not digs and exp < 0:
exp = 0
return Decimal((sign, digs, exp))
Why not use modules 10 from a multiple of 10 to check if there is remainder? No remainder means you can force int()
if (x * 10) % 10 == 0:
x = int(x)
x = 2/1
Output: 2
x = 3/2
Output: 1.5