Many questions have been asked on StackOverflow and elsewhere about Python's confusing behaviour with calculations which use floats - often returning a result which is clearly wrong by a small amount. The explanation for this is invariably linked to. A practical simple solution is not usually provided however.
It isn't just the error (which is usually negligible) - it is more the mess and inelegance of getting a result like 3.999999999999999 for a simple sum like 8.7 - 4.7.
I have written a simple solution for this, and my question is, why isn't sthg like this automatically implemented by Python behind the scenes?
The basic concept is to convert all floats into integers, to do the operation, and then convert back appropriately into a float. The difficulties explained in the above-linked doc only apply to floats, not to ints, which is why it works. Here is the code:
def justwork(x,operator,y):
numx = numy = 0
if "." in str(x):
numx = len(str(x)) - str(x).find(".") -1
if "." in str(y):
numy = len(str(y)) - str(y).find(".") -1
num = max(numx,numy)
factor = 10 ** num
newx = x * factor
newy = y * factor
if operator == "%":
ans1 = x % y
ans = (newx % newy) / factor
elif operator == "*":
ans1 = x * y
ans = (newx * newy) / (factor**2)
elif operator == "-":
ans1 = x - y
ans = (newx - newy) / factor
elif operator == "+":
ans1 = x + y
ans = (newx + newy) / factor
elif operator == "/":
ans1 = x / y
ans = (newx / newy)
elif operator == "//":
ans1 = x // y
ans = (newx // newy)
return (ans, ans1)
This is admittedly rather inelegant and could probably be improved with a bit of thought, but it gets the job done. The function returns a tuple with the correct result (by converting to integer), and the incorrect result (automatically provided). Here are examples of how this provides accurate results, as opposed to doing it normally.
#code #returns tuple with (correct, incorrect) result
print(justwork(0.7,"%",0.1)) #(0.0, 0.09999999999999992)
print(justwork(0.7,"*",0.1)) #(0.07, 0.06999999999999999)
print(justwork(0.7,"-",0.2)) #(0.5, 0.49999999999999994)
print(justwork(0.7,"+",0.1)) #(0.8, 0.7999999999999999)
print(justwork(0.7,"/",0.1)) #(7.0, 6.999999999999999)
print(justwork(0.7,"//",0.1)) #(7.0, 6.0)
TLDR: Essentially the question is, Why are floats stored as base 2 binary fractions (which are inherently imprecise) when they could be stored the same way as integers (which Just Work)?
Three points:
the function in the question/general method proposed, while it does avoid the problem in many cases, there are many other cases, even relatively simple ones, where it has the same problem.
there is a decimal module which always provides accurate answers (even when the justwork() function in the question fails to)
using the decimal module slows things down considerably - taking roughly 100 times longer. The default approach sacrifices accuracy to prioritise speed. [Whether making this the default is the right approach is debatable].
To illustrate these three points consider the following functions, loosely based on that in the question:
def justdoesntwork(x,operator,y):
numx = numy = 0
if "." in str(x):
numx = len(str(x)) - str(x).find(".") -1
if "." in str(y):
numy = len(str(y)) - str(y).find(".") -1
factor = 10 ** max(numx,numy)
newx = x * factor
newy = y * factor
if operator == "+": myAns = (newx + newy) / factor
elif operator == "-": myAns = (newx - newy) / factor
elif operator == "*": myAns = (newx * newy) / (factor**2)
elif operator == "/": myAns = (newx / newy)
elif operator == "//": myAns = (newx //newy)
elif operator == "%": myAns = (newx % newy) / factor
return myAns
and
from decimal import Decimal
def doeswork(x,operator,y):
if operator == "+": decAns = Decimal(str(x)) + Decimal(str(y))
elif operator == "-": decAns = Decimal(str(x)) - Decimal(str(y))
elif operator == "*": decAns = Decimal(str(x)) * Decimal(str(y))
elif operator == "/": decAns = Decimal(str(x)) / Decimal(str(y))
elif operator == "//": decAns = Decimal(str(x)) //Decimal(str(y))
elif operator == "%": decAns = Decimal(str(x)) % Decimal(str(y))
return decAns
and then looping through many values to find where myAns is different to decAns:
operatorlist = ["+", "-", "*", "/", "//", "%"]
for a in range(1,1000):
x = a/10
for b in range(1,1000):
y=b/10
counter = 0
for operator in operatorlist:
myAns, decAns = justdoesntwork(x, operator, y), doeswork(x, operator, y)
if (float(decAns) != myAns) and len(str(decAns)) < 5 :
print(x,"\t", operator, " \t ", y, " \t= ", decAns, "\t\t{", myAns, "}")
=> this goes through all values to 1 d.p. from 0.1 to 99.9 - and indeed fails to find any values where myAns is different to decAns.
However if it is changed to give 2d.p. (i.e. either x = a/100 or y = b/100), then many examples appear. For example, 0.1+1.09 - this can easily be checked by typing in the console ((0.1*100)+(1.09*100)) / (100), which uses the basic method of the question, and which returns 1.1900000000000002 instead of 1.19. The source of the error is in 1.09*100 which returns 109.00000000000001. [Simply typing in 0.1+1.09 also gives the same error]. So the approach suggested in the question doesn't always work.
Using Decimal() however returns the correct answer: Decimal('0.1')+Decimal('1.09') returns Decimal('1.19').
[Note: Don't forget to enclose the 0.1 and 1.09 with quotes. If you don't, Decimal(0.1)+Decimal(1.09) returns Decimal('1.190000000000000085487172896') - because it starts with a float 0.1 which is stored inaccurately, and then converts that to Decimal - GIGO. Decimal() has to be fed a string. Taking a float, converting it to a string, and from there to Decimal, does seem to work though, the problem is only when going directly from float to Decimal].
In terms of time cost, run this:
import timeit
operatorlist = ["+", "-", "*", "/", "//", "%"]
for operator in operatorlist:
for a in range(1,10):
a=a/10
for b in range(1,10):
b=b/10
DECtime = timeit.timeit("Decimal('" +str(a)+ "') " +operator+ " Decimal('" +str(b)+ "')", setup="from decimal import Decimal")
NORMtime = timeit.timeit(str(a) +operator+ str(b))
timeslonger = DECtime // NORMtime
print("Operation: ", str(a) +operator +str(b) , "\tNormal operation time: ", NORMtime, "\tDecimal operation time: ", DECtime, "\tSo Decimal operation took ", timeslonger, " times longer")
This shows that Decimal operations consistently take around 100 times longer, for all the operators tested.
[Including exponentiation in the list of operators shows that exponentiation can take 3000 - 5000 times longer. However this is partly because Decimal() evaluates to far greater precision than normal operations - Decimal() default precision is 28 places - Decimal("1.5")**Decimal("1.5") returns 1.837117307087383573647963056, whereas 1.5**1.5 returns 1.8371173070873836. If you limit b to whole numbers by replacing b=b/10 with b=float(b) (which will prevent results with high SFs), the Decimal calculation takes around 100 times longer, as with other operators].
It could still be argued that the time cost is only significant for users performing billions of calculations, and most users would prioritise getting intelligible results over a time difference which is pretty insignificant in most modest applications.
Related
How do I make an operator a variable? for example I want to store the value 1 in operator '+' that is "+" = 1. But python is showing an error, what do I do?
my project is this: while True:
current_number = int(input("Enter the number that you wish to be displayed: "))
print(f"The current displayed number is: {current_number}")
print("The value of '+' and '-' is limited to 5")
n = input()
if n == "+" or "++" or "+++" or "++++" or "+++++":
if n == "+":
print(current_number + 1)
if n == "++":
print(current_number + 2)
if n == "+++":
print(current_number + 3)
if n == "++++":
print(current_number + 4)
if n == "+++++":
print(current_number + 5)
elif n == "-" or "--" or "---" or "----" or "-----":
if n == "-":
print(current_number - 1)
if n == "--":
print(current_number - 2)
if n == "---":
print(current_number - 3)
if n == "----":
print(current_number - 4)
if n == "-----":
print(current_number - 5)
I want to simplify the code by making "+" = 1, how do I do it?
Use len() to count the number of characters in n:
while True:
current_number = int(input("Enter the number that you wish to be displayed: "))
print(f"The current displayed number is: {current_number}")
n = input()
if set(n) == {"+"}:
print(current_number + len(n))
elif set(n) == {"-"}:
print(current_number - len(n))
Enter the number that you wish to be displayed: 37
The current displayed number is: 37
+++++
42
Note that with this approach there's no need to arbitrarily limit the number of characters, although you can still do that explicitly by rejecting inputs where len(n) > 5.
Your original version of the check for if the string contains all "+" or "-" doesn't work:
if n == "+" or "++" or "+++" or "++++" or "+++++":
because (n == "+") or ("++") will simply return "++" (which is true) if n == "+" is not True. A "correct" way to write this check would be:
if n in ("+", "++", "+++", "++++", "+++++"):
or more simply (since these specific strings are all substrings of "+++++":
if n in "+++++":
My version of the code does this instead:
if set(n) == {"+"}:
which works by converting n to a set (reducing it to only the unique characters) -- if n contains all "+"s, then its set is {"+"}. This works for any length of n.
Things other than [A-Za-z0-9_] are not allowed as names of variables
If you 100% need that, use a dictionary
ie
specialVars={'+':1, "-":-1}
then call
specialVars['+']
edit: if you jut need to add 1 for each '+' and -1 for each '-'
do this
print(current_number+n.count('+')-n.count('-'))
Trying to find the smallest positive integer n such that n! divided by the sum of the digits of n! has a remainder.
Two people approached the problem in similar ways but got different answers. I've read that % and fmod behave differently with negative numbers but there are no negative numbers involved here. Large number calculators online seem to show that the solution arrived at through % is correct.
Using mod operator:
import math
n=1
def func():
global n
intn=[]
#for loop creates a list of integers of the digits of n!
for digit in str(math.factorial(n)):
intn.append(int(digit))
denominator=sum(intn)
#if n!/denominator has a remainder, print n; the program is over. Otherwise, increase n and try again.
if (math.factorial(n))%denominator:
print(n)
else:
n+=1
func()
func()
Changing if (math.factorial(n))%(denominator): to if int((math.factorial(n)))%int(denominator): changed the result so that it is the same as when using .fmod, but again, I don't think that is the correct answer.
Using math.fmod:
import math
# curNum is the variable I'm using to track the integer whose value I'm testing
curNum = 1
# conditionMet is a boolean that will be used to break the while loop of the test
conditionMet = False
while conditionMet == False:
sumDigits = 0
curFactorial = math.factorial(curNum)
curFactorialAsString = str(curFactorial)
#sumDigits = sum(int(curNumAsString))
for curDigit in curFactorialAsString:
sumDigits = sumDigits + int(curDigit)
if math.fmod(curFactorial,sumDigits) != 0:
print("curNum: " + str(curNum) + "curFactorial: " + str(curFactorial) + "sumDigits: " + str(sumDigits))
conditionMet = true
else:
curNum = curNum + 1
What are some approaches to evaluate an expression such as ((1 + 1) * 2) - 3?
Right now I am converting it to postfix form 1 1 + 2 * 3 - and then evaluate that one.
I'd like to know if there are better approaches than this.
This should work, but the Expression must be between parentheses
for example : ((4-6)+(7/9)+3) should be (((4-6)+(7/9))+3)
def evalu(express):
operat,numbers=[],[]
for i in express:
if i =='(':
pass
elif i in '0123456789':
numbers.append(int(i))
elif i in "+*-/":
operat.append(i)
elif i==")" :
oper=operat.pop()
if oper =='+':numbers.append(numbers.pop()+numbers.pop())
elif oper =='*':numbers.append(numbers.pop()*numbers.pop())
elif oper =='/':numbers.append(numbers.pop(-2)/numbers.pop())
elif oper =='-':numbers.append(numbers.pop(-2)-numbers.pop())
return numbers.pop()
I'm relatively new to python, and decided to make a calculator. Unfortunately I cant solve for x. The error is:
SyntaxError: can use starred expression only as assignment target.
I can't figure out any way around this as I want to have the person enter in the problem, and then it prints x.
Please help, And thanks for any help in advance.
My code:
import random
from datetime import datetime
import time
def ints(x,y):
x = int(x)
y = int(y)
now = datetime.now()
def solve(c, z):
c = (*z)
print(now.year)
time.sleep(1)
print("WELCOME TO THE JERAXXUS SOFTWARE")
time.sleep(2)
math = True
if math == True:
user_input = input("My name is jeraxxus, please put in 2 numbers followed by a operator, *, /, +, -, **, or %. No commas please")
user_input = str.split(user_input)
a_list = [user_input]
n1 = user_input[0]
n2 = user_input[1]
operate = user_input[2]
algebra = input("does your mathmatical equation contain algebraic values?")
if algebra == 'no':
if operate == '*':
n1 = int(n1)
n2 = int(n2)
print(n1 * n2)
elif operate == '/':
n1 = int(n1)
n2 = int(n2)
print(n1 / n2)
elif operate == '+':
n1 = int(n1)
n2 = int(n2)
print(n1 + n2)
elif operate == '-':
n1 = int(n1)
n2 = int(n2)
print(n1 - n2)
elif operate == '**':
n1 = int(n1)
n2 = int(n2)
print(n1 ** n2)
elif operate == '%':
n1 = int(n1)
n2 = int(n2)
print(n1 % n2)
elif operate != '%' and operate!= '**' and operate != '-' and operate != '+' and operate != '/' and operate != '*':
print("SHAME YOU SHOULD HAVE FOLLOWED MY COMMANDS")
math = False
elif algebra == 'yes':
problem = input("please state your algebraic problems with spaces after each operation and number, the order is crucial please have only 1 variable and have it first.")
problem = str.split(problem)
lop = problem[0]
b_list = [problem]
sovle(lop, b_list)
here are a few thing about your code:
the function ints do nothing in the end because you don't return any value, the error that you get come from c=(*z) you can't do that in a assignation, but you can do it in a function call like this fun(*argument_list).
The variable math as used there is useless because you assign it True and check it for that same value, so you enter in that if block unconditionally meaning that that if math == True is unneeded, maybe you mean while math that way you repeat that block while the variable math is true.
What is the reason of the variable a_list?, you don't use it.
In the block if algebra == 'no' you can put int conversions first and then check the operate so to avoid repeat the same code over and over again, speaking of operate the last elif is redundant because if you get there it is because it fail the other comparisons so no need to check again against every possibility, change it for a simple else.
with that little revisions you code will look like this
import random
from datetime import datetime
import time
def solve(c, z):
raise NotImplementedError("In process of programming") # an error because there is still no code for this
now = datetime.now()
print(now.year)
time.sleep(1)
print("WELCOME TO THE JERAXXUS SOFTWARE")
time.sleep(2)
math = True
while math:
user_input = input("My name is jeraxxus, please put in 2 numbers followed by a operator, *, /, +, -, **, or %. No commas please")
user_input = str.split(user_input)
#a_list = [user_input]
n1 = user_input[0]
n2 = user_input[1]
operate = user_input[2]
algebra = input("does your mathematical equation contain algebraic values?")
if algebra == 'no':
n1 = int(n1)
n2 = int(n2)
if operate == '*':
print(n1 * n2)
elif operate == '/':
print(n1 / n2)
elif operate == '+':
print(n1 + n2)
elif operate == '-':
print(n1 - n2)
elif operate == '**':
print(n1 ** n2)
elif operate == '%':
print(n1 % n2)
else:
print("SHAME YOU SHOULD HAVE FOLLOWED MY COMMANDS")
math = False
elif algebra == 'yes':
problem = input("please state your algebraic problems with spaces after each operation and number, the order is crucial please have only 1 variable and have it first.")
problem = str.split(problem)
lop = problem[0]
b_list = [problem]
solve(lop, b_list)
The no algebra part work fine, you need now figure the solve x part, if you need help with this too, just ask :)
Simple calculator
After quick search, making a simple calculator in python is easy with the use of eval, like this
def calculator():
exp = input("x= ")
print("x=", eval(exp) )
with this you can procesate any valid python expression as if you were in the IDLE.
But if for academics reason you don't want to use it, then as I tell you before, you have to make a parser of mathematical expressions, that identificate what operator is in there and arrange them according to its precedence and finally solve the expresion
Since factoring a quadratic equation in my head just happens, and has done that since I learned it - how would I go about starting to write a quadratic factorer in Python?
Improving Keiths's answer:
Start with a polynomial P(x) = a*x^2 + b*x + c.
Use the quadratic formula (or another method of your choice) to find the roots r1 and r2 to P(x) = 0.
You can now factor P(x) as a*(x-r1)(x-r2).
If your factor (3x - 4)(x - 9) the solution will be 3*(x - 4/3)(x - 9).
You might want to find a way to multiply the 3 into the factors to get rid of fractions / look pretty. In this case, it might help to use fraction arithmetic instead of doubles so you can know the denominators better.
Use the quadratic formula.
I tried implementing hugomg's approach. I stole the "gcd" and "simplify fraction" function from online. Here is my sloppy approach:
from math import sqrt
def gcd(a, b):
while b:
a, b = b, a % b
return a
def simplify_fraction(numer, denom):
if denom == 0:
return "Division by 0 - result undefined"
# Remove greatest common divisor:
common_divisor = gcd(numer, denom)
(reduced_num, reduced_den) = (numer / common_divisor, denom / common_divisor)
# Note that reduced_den > 0 as documented in the gcd function.
if common_divisor == 1:
return (numer, denom)
else:
# Bunch of nonsense to make sure denominator is negative if possible
if (reduced_den > denom):
if (reduced_den * reduced_num < 0):
return(-reduced_num, -reduced_den)
else:
return (reduced_num, reduced_den)
else:
return (reduced_num, reduced_den)
def quadratic_function(a,b,c):
if (b**2-4*a*c >= 0):
x1 = (-b+sqrt(b**2-4*a*c))/(2*a)
x2 = (-b-sqrt(b**2-4*a*c))/(2*a)
# Added a "-" to these next 2 values because they would be moved to the other side of the equation
mult1 = -x1 * a
mult2 = -x2 * a
(num1,den1) = simplify_fraction(a,mult1)
(num2,den2) = simplify_fraction(a,mult2)
if ((num1 > a) or (num2 > a)):
# simplify fraction will make too large of num and denom to try to make a sqrt work
print("No factorization")
else:
# Getting ready to make the print look nice
if (den1 > 0):
sign1 = "+"
else:
sign1 = ""
if (den2 > 0):
sign2 = "+"
else:
sign2 = ""
print("({}x{}{})({}x{}{})".format(int(num1),sign1,int(den1),int(num2),sign2,int(den2)))
else:
# if the part under the sqrt is negative, you have a solution with i
print("Solutions are imaginary")
return
# This function takes in a, b, and c from the equation:
# ax^2 + bx + c
# and prints out the factorization if there is one
quadratic_function(7,27,-4)
If I run this I get the output:
(7x-1)(1x+4)