Floating point modulus problem - python

I'm having a problem with modulus on a floating point number in Python. This code:
...
print '(' + repr(olddir) + ' + ' + repr(self.colsize) + ') % (math.pi*2) = ' + repr((olddir+self.colsize)
...
Prints:
(6.281876310240881 + 0.001308996938995747) % (math.pi*2) = 2.9043434324194095e-13
I know floating point numbers aren't precise. But I can't get this to make any sense.
I don't know if it is in any way related but Google Calculator can't handle this calculation either. This is the output from Google Calculator:
(6.28187631024 + 0.001308996939) % (pi * 2) = 6.28318531
What is causing this calculation error? And how can I avoid it in my Python program?

Using str() to print a floating point number actually prints a rounded version of the number:
>>> print repr(math.pi)
3.1415926535897931
>>> print str(math.pi)
3.14159265359
So we can't really reproduce your results, since we don't know the exact values you are doing the computation with. Obviously, the exact value of olddir+self.colsize is slightly greater than 2*math.pi, while the sum of the rounded values you used in Google Calculator is slightly less than 2*math.pi.

The difference between str and repr
>>> import scipy
>>> pi = scipy.pi
>>> str(pi)
'3.14159265359'
>>> repr(pi)
'3.1415926535897931'
str truncates floating point numbers to 12 digits, where repr gives the internal representation (as a string).
EDIT: So in summary, the problem arose because you rounded prematurely and are calculating the modulus of something via a number that's very close to it. With floating point numbers, rounding is inevitably involved in converting decimal numbers into binary.
First, do an example of how rounding hurts you with actual math (not floating point math). Look at (3.14+3.14) % (3.14+3.14), which is obviously zero. Now what would happen if we rounded the digits to one decimal digit first on one side? Well (3.1+3.1) % (3.14+3.14) = 6.2 % (6.28) = 6.2 (what google gave you). Or if you did round(3.14159,5) + round(3.14159,5) % (3.14159 + 3.14159) = 6.2832 % 6.28318 = 2e-5.
So in by rounding to N digits (by using str which effectively rounds the numbers), your calculation is only accurate to less than N digits. To have this work going forward force rounding at some higher digit (keeping two calculated digits for safety) is necessary. E.g., str rounds at digit 12, so maybe we should round at digit 10.
>>> round(6.28187631024 + 0.001308996939,10) % (round(pi * 2,10))
0

Related

rounding floats in python (handling 5s)

I am trying figure out the best way to robustly round floats in python using the round half up algorithm. It seems the best way to do this is using the decimal library. However I would expect this method to carry over the rounding up of a 5 across a float. For example:
from decimal import *
Decimal('3.445').quantize(Decimal('0.1'), rounding=ROUND_HALF_UP)
The result is 3.4. What I would expect the algorithm to do is carry over the round up of the 5 such that 3.445 = 3.45 = 3.5.
Does anybody know how to do this in python? I cannot seem to find a robust way of doing this.
Rounding half up doesn't work by carrying over the round-ups from lower digits, but simply determines the half point at the given exponent. Since 3.445 % 0.1 == 0.045, which is less than half of 0.1, it would correctly round down to 3.4.
You can instead implement the desired rounding logic by rounding half up the given decimal number from the second-least significant digit to the given target exponent in a loop:
def round_half_up_carryover(d, target_exp):
exp = Decimal(10) ** (d.as_tuple()[2] + 1)
while exp <= target_exp:
d = d.quantize(exp.normalize(), rounding=ROUND_HALF_UP)
exp *= 10
return d
so that:
print(round_half_up_carryover(Decimal('3.445'), Decimal('0.1')))
would output:
3.5

Overcoming the binary incompetence in regards to fractions

I am trying to add fractions together (1/5 and such), and since binary is unable to add certain fractions, I'm getting really horrible answers like 2.99999999999996 and so on. I understand I can use math.round (since pythons round is not actually round) to get it to 3, but I don't want that.
Is there any module I can use to add two numbers together (such as 0.2 and 0.2) and get a result of something other then 0.39999999999999999999999996?
EDIT:
I tried using decimal, the result I got from this after inputting 6, 12, 24 and 24 is 2.9999999999:
while True:
try:
x += 1
print("\n" + "Enter resistance of resistor " + str(x))
resistors.append(int(input()))
except:
temp = Decimal(0)
print(temp)
for i in resistors:
print(temp)
temp += Decimal(1)/Decimal(i)
temp = temp**-1
print("\n" + "The total resistance of inputted resistors in parallel is: " + str(temp))
input()
break
It's not "binary incompetence". It is a result of using real machines to represent idealized mathematical entities. There are an infinite number of real numbers between 0 and 1. Whatever representation you use, binary, ternary, octal, decimal or hex, if you have only a finite number of digits, then some of those real numbers are going to have to share the same representation. You can't represent 1/100 accurately in binary because it is a recurring binary fraction. Just as you can't represent 1/3 in decimal because it is a recurring decimal fraction. But when your 4-function pocket calculator shows 1/3 as 0.3333333 you don't call that decimal incompetence.
If you want to represent rational numbers accurately then you can use the fractions module:
>>> import fractions
>>> fifth = fractions.Fraction(1,5)
>>> fifth * 2
Fraction(2, 5)
But when you convert Fraction(2,5) to a float, you go back to floating point representation.
I think that not is the real issue, though. I think you just need to fix this line:
print("\n" + "The total resistance of inputted resistors in parallel is: " + str(temp))
That is giving you the default string representation of the number, which shows the closest possible representation to the highest possible precision. But you actually want to see a nice number:
print("\n" + "The total resistance of inputted resistors in parallel is: %g" % temp)

Python Rounding Inconsistently

If I tell Python v. 3.4.3, round(2.5), then it outputs 2. If I tell it round(1.5) then it outputs 2 as well, though. Similarly, round(3.5) gives 4, while round(4.5) gives 4 as well. I need Python to round with consistency, though. Specifically, it needs to round anytime I input a number halfway between two integers. So round(1.5) = 1 and round(2.5) = 2, while round(1.6) = 2 and such, as usual.
How can I resolve this?
EDIT: I've already read the documentation for the round function and understand that this is its intended behavior. My question is, how can I alter this behavior, because for my purposes I need 1.5 round down.
Python 3 uses a different rounding behaviour compared to Python 2: it now uses so-called "banker's rounding" (Wikipedia): when the integer part is odd, the number is rounded away from zero; when the integer part is even, is it rounded towards zero.
The reason for this is to avoid a bias, when all values at .5 are rounded away from zero (and then e.g. summed).
This is the behaviour you are seeing, and it is in fact consistent. It's perhaps just different than what you are used to.
The round docs do address the peculiaries of rounding floating point numbers.
You can use the decimal library to achieve what you want.
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN
round(2.675, 2)
# output: 2.67
Decimal('2.675').quantize(Decimal('1.11'), rounding=ROUND_HALF_UP)
# output: 2.68
Decimal('2.5').quantize(Decimal('1.'), rounding=ROUND_HALF_DOWN)
# output: 2
Your want "round down", and you are getting "round to even". Just do it manually by doing
ceil(x - 0.5)
This is documented pretty well. According to the Python docs for round:
Note The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float. See Floating Point Arithmetic: Issues and Limitations for more information
In specific, this is a side-effect of how computers handle floating-point numbers in general.
If you need more precision, including different rounding, I suggest you check out the Python Decimal module. Specifically of interest, they have the ability to control rounding modes. Looks like you might want decimal.ROUND_HALF_DOWN.
Python 3 provides rounding methods defined in the IEEE Standard for Floating-Point Arithmetic (IEEE 754), the default rounding[1] is directed to the nearest number and minimizing cumulative errors.
In IEEE 754, there are 5 methods defined, two for rounding to nearest (Python provides the first one by round) and three methods that are explicitly directed (Python has trunc, ceil, and floor in its Math module).
You obviously need a directed rounding and there is a way to tell this Python, you have just to choose.
[1] Since the representation of floating point numbers in computers is limited, rounding is not as trivial as you might think, you'll be surprised! I recommend a careful read of 15. Floating Point Arithmetic: Issues and Limitations in the python 3 documentation.
I believe I have the answer to all the rounding errors people have been encountering. I have wrote my own method, which functions same as the "round" but actually looks at the last digit and rounds from there case by case. There is no converting a decimal to binary. It can handle any amount of numbers behind the decimal and it also takes scientific notation (as outputted by floats). It also doesn't require any imports! Let me know if you catch any cases that don't work!
def Round(Number,digits = 0):
Number_Str = str(Number)
if "e" in Number_Str:
Number_Str = "%.10f" % float(Number_Str)
if "." in Number_Str: #If not given an integer
try:
Number_List = list(Number_Str) #All the characters in Number in a list
Number_To_Check = Number_List[Number_List.index(".") + 1 + digits] #Gets value to be looked at for rounding.
if int(Number_To_Check) >= 5:
Index_Number_To_Round = Number_List.index(".") + digits
if Number_List[Index_Number_To_Round] == ".":
Index_Number_To_Round -= 1
if int(Number_List[Index_Number_To_Round]) == 9:
Number_List_Spliced = Number_List[:Number_List.index(".")+digits]
for index in range(-1,-len(Number_List_Spliced) - 1,-1):
if Number_List_Spliced[index] == ".":
continue
elif int(Number_List_Spliced[index]) == 9:
Number_List_Spliced[index] = "0"
try:
Number_List_Spliced[index-1]
continue
except IndexError:
Number_List_Spliced.insert(0,"1")
else:
Number_List_Spliced[index] = str(int(Number_List_Spliced[index])+1)
break
FinalNumber = "".join(Number_List_Spliced)
else:
Number_List[Index_Number_To_Round] = str(int(Number_List[Index_Number_To_Round])+1)
FinalNumber = "".join(Number_List[:Index_Number_To_Round + 1])
return float(FinalNumber)
else:
FinalNumber = "".join(Number_List[:Number_List.index(".") + 1 + digits])
return float(FinalNumber)
except IndexError:
return float(Number)
else: #If given an integer
return float(Number)

How to print floating point numbers as it is without any truncation in python?

I have some number 0.0000002345E^-60. I want to print the floating point value as it is.
What is the way to do it?
print %f truncates it to 6 digits. Also %n.nf gives fixed numbers. What is the way to print without truncation.
Like this?
>>> print('{:.100f}'.format(0.0000002345E-60))
0.0000000000000000000000000000000000000000000000000000000000000000002344999999999999860343602938602754
As you might notice from the output, it’s not really that clear how you want to do it. Due to the float representation you lose precision and can’t really represent the number precisely. As such it’s not really clear where you want the number to stop displaying.
Also note that the exponential representation is often used to more explicitly show the number of significant digits the number has.
You could also use decimal to not lose the precision due to binary float truncation:
>>> from decimal import Decimal
>>> d = Decimal('0.0000002345E-60')
>>> p = abs(d.as_tuple().exponent)
>>> print(('{:.%df}' % p).format(d))
0.0000000000000000000000000000000000000000000000000000000000000000002345
You can use decimal.Decimal:
>>> from decimal import Decimal
>>> str(Decimal(0.0000002345e-60))
'2.344999999999999860343602938602754401109865640550232148836753621775217856801120686600683401464097113374472942165409862789978024748827516129306833728589548440037314681709534891496105046826414763927459716796875E-67'
This is the actual value of float created by literal 0.0000002345e-60. Its value is a number representable as python float which is closest to actual 0.0000002345 * 10**-60.
float should be generally used for approximate calculations. If you want accurate results you should use something else, like mentioned Decimal.
If I understand, you want to print a float?
The problem is, you cannot print a float.
You can only print a string representation of a float. So, in short, you cannot print a float, that is your answer.
If you accept that you need to print a string representation of a float, and your question is how specify your preferred format for the string representations of your floats, then judging by the comments you have been very unclear in your question.
If you would like to print the string representations of your floats in exponent notation, then the format specification language allows this:
{:g} or {:G}, depending whether or not you want the E in the output to be capitalized). This gets around the default precision for e and E types, which leads to unwanted trailing 0s in the part before the exponent symbol.
Assuming your value is my_float, "{:G}".format(my_float) would print the output the way that the Python interpreter prints it. You could probably just print the number without any formatting and get the same exact result.
If your goal is to print the string representation of the float with its current precision, in non-exponentiated form, User poke describes a good way to do this by casting the float to a Decimal object.
If, for some reason, you do not want to do this, you can do something like is mentioned in this answer. However, you should set 'max_digits' to sys.float_info.max_10_exp, instead of 14 used in the answer. This requires you to import sys at some point prior in the code.
A full example of this would be:
import math
import sys
def precision_and_scale(x):
max_digits = sys.float_info.max_10_exp
int_part = int(abs(x))
magnitude = 1 if int_part == 0 else int(math.log10(int_part)) + 1
if magnitude >= max_digits:
return (magnitude, 0)
frac_part = abs(x) - int_part
multiplier = 10 ** (max_digits - magnitude)
frac_digits = multiplier + int(multiplier * frac_part + 0.5)
while frac_digits % 10 == 0:
frac_digits /= 10
scale = int(math.log10(frac_digits))
return (magnitude + scale, scale)
f = 0.0000002345E^-60
p, s = precision_and_scale(f)
print "{:.{p}f}".format(f, p=p)
But I think the method involving casting to Decimal is probably better, overall.

Decimal precision in python without decimal module

I'm fairly new to Python, and was wondering how would I be able to control the decimal precision of any given number without using any the decimal module or floating points (eg: " %4f" %n).
Examples (edit):
input(2/7)
0.28571428571....
input(1/3)
0.33333333333333....
and I wanted them to thousand decimal points or any decimal point for that matter. I was thinking of using a while as a controlled loop, but I'm not really sure how to do so. Thanks
edit: The reason why I'm not using the decimal module is just so I can conceptualize the algorithm/logic behind these type of things. Just trying to really understand the logic behind things.
We can use a long to store a decimal with high precision, and do arithmetic on it. Here's how you'd print it out:
def print_decimal(val, prec):
intp, fracp = divmod(val, 10**prec)
print str(intp) + '.' + str(fracp).zfill(prec)
Usage:
>>> prec = 1000
>>> a = 2 * 10**prec
>>> b = a//7
>>> print_decimal(b, prec)
0.2857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857
Without the Decimal module (why, though?), assuming Python 3:
def divide(num, den, prec):
a = (num*10**prec) // den
s = str(a).zfill(prec+1)
return s[0:-prec] + "." + s[-prec:]
Thanks to #nneonneo for the clever .zfill() idea!
>>> divide(2,7,1000)
'0.28571428571428571428571428571428571428571428571428571428571428571428571428571
42857142857142857142857142857142857142857142857142857142857142857142857142857142
85714285714285714285714285714285714285714285714285714285714285714285714285714285
71428571428571428571428571428571428571428571428571428571428571428571428571428571
42857142857142857142857142857142857142857142857142857142857142857142857142857142
85714285714285714285714285714285714285714285714285714285714285714285714285714285
71428571428571428571428571428571428571428571428571428571428571428571428571428571
42857142857142857142857142857142857142857142857142857142857142857142857142857142
85714285714285714285714285714285714285714285714285714285714285714285714285714285
71428571428571428571428571428571428571428571428571428571428571428571428571428571
42857142857142857142857142857142857142857142857142857142857142857142857142857142
85714285714285714285714285714285714285714285714285714285714285714285714285714285
7142857142857142857142857142857142857142857'
Caveat: This uses floor division, so divide(2,3,2) will give you 0.66 instead of 0.67.
While the other answers use very large values to handle the precision, this implements long division.
def divide(num, denom, prec=30, return_remainder=False):
"long divison"
remain=lim=0
digits=[]
#whole part
for i in str(num):
d=0;remain*=10
remain+=int(i)
while denom*d<=remain:d+=1
if denom*d>remain:d-=1
remain-=denom*d
digits.append(d)
#fractional part
if remain:digits.append('.')
while remain and lim<prec:
d=0;remain*=10
while denom*d<=remain:d+=1
if denom*d>remain:d-=1
remain-=denom*d
digits.append(d)
lim+=1
#trim leading zeros
while digits[0]==0 and digits[1]!='.':
digits=digits[1:]
quotient = ''.join(list(map(str,digits)))
if return_remainder:
return (quotient, remain)
else:
return quotient
Because it's the division algorithm, every digit will be correct and you can get the remainder (unlike floor division which won't have the remainder). The precision here I've implemented as the number of digits after the decimal sign.
>>> divide(2,7,70)
'0.2857142857142857142857142857142857142857142857142857142857142857142857'
>>> divide(2,7,70,True)
('0.2857142857142857142857142857142857142857142857142857142857142857142857', 1)

Categories