How do I compare large numbers to small numbers in python 3? - python-3.x

I have to use the math.exp() function to get the following value for x which is converted to scientific notation. However, when trying to compare to see if this number is greater than y, python thinks it is less than.
x = 4.0686596698143466e+186
y = 59425800000000000000000
if x >= y:
print:("x is greater than y")
I realize there are methods to turn very large numbers into smaller int but I feel that route is a little more complicated and above my learning curve than necessary. I just need a way to see if x > y and also curious why python doesn't support the comparison. Converting y to scientific notation using decimal only turns it into a string.
Disclaimer: still a beginner

Related

Changing printing format for fractions using sympy and PythonTex

Below is a minimal working problem, of what I am working on.
The file is a standard LaTeX file using sympy within pythontex, where I want to change
how sympy displays fractions.
Concretely I would like to make the following changes, but have been struggling:
How can I make sympy display the full, and not inline version of it's fractions for some of its fractions? In particular I would like the last fraction 1/5 to instead be displayed in full.
eg. \fraction{1}{5}
In the expression for the derivative, I have simplified the results, but I struggle to substitute the variable x with the fraction a/b. Whenever I substitute this expression into the fraction it fully simplifies the expression, which is not what I want. I just want to replace x with the fraction a/b (in this case 2/3 or 1/3 depending on the seed).
Below I have attached two images displaying what my code produces, and what I would like it to display. Do note that this is also stated in the two bullets above
Current output
Desired output
Code
\documentclass{article}
\usepackage{pythontex}
\usepackage{mathtools,amssymb}
\usepackage{amsmath}
\usepackage{enumitem}
\begin{document}
\begin{pycode}
import math
from sympy import *
from random import randint, seed
seed(2021)
\end{pycode}
\paragraph{Oppgave 3}
\begin{pycode}
a, b = randint(1,2), 3
ab = Rational(a,b)
pressure_num = lambda x: 1-x
pressure_denom = lambda x: 1+x
def pressure(x):
return (1-x)/(1+x)
pressure_ab = Rational(pressure_num(ab),pressure_denom(ab))
x, y, z = symbols('x y z')
pressure_derivative = simplify(diff(pressure(x), x))
pressure_derivative_ab = pressure_derivative.xreplace({ x : Rational(a,b)})
\end{pycode}
The partial pressure of some reaction is given as
%
\begin{pycode}
print(r"\begin{align*}")
print(r"\rho(\zeta)")
print(r"=")
print(latex(pressure(Symbol('\zeta'))))
print(r"\qquad \text{for} \ 0 \leq \zeta \leq 1.")
print(r"\end{align*}")
\end{pycode}
%
\begin{enumerate}[label=\alph*)]
\item Evaluate $\rho(\py{a}/\py{b})$. Give a physical interpretation of your
answer.
\begin{equation*}
\rho(\py{a}/\py{b})
= \frac{1-(\py{ab})}{1+\py{ab}}
= \frac{\py{pressure_num(ab)}}{\py{pressure_denom(ab)}}
\cdot \frac{\py{b}}{\py{b}}
= \py{pressure_ab}
\end{equation*}
\end{enumerate}
The derivative is given as
%
\begin{pycode}
print(r"\begin{align*}")
print(r"\rho'({})".format(ab))
print(r"=")
print(latex(pressure_derivative))
print(r"=")
print(latex(simplify(pressure_derivative_ab)))
print(r"\end{align*}")
\end{pycode}
\end{document}
Whenever I substitute this expression into the fraction it fully simplifies the expression, which is not what I want. I just want to replace x with the fraction a/b (in this case 2/3 or 1/3 depending on the seed).
It's possible to do this, if we use a with expression to temporarily disable evaluation for that code block, and then we use two dummy variables in order to represent the fraction, and finally we do the substitution with numerical values.
So the following line in your code:
pressure_derivative_ab = pressure_derivative.xreplace({ x : Rational(a,b)})
can be changed to:
with evaluate(False):
a1,b1=Dummy('a'),Dummy('b')
pressure_derivative_ab = pressure_derivative.subs(x,a1/b1).subs({a1: a,b1: b})
The expressions pressure_derivative and pressure_derivative_ab after this are:
How can I make sympy display the full, and not inline version of it's fractions for some of its fractions? In particular I would like the last fraction 1/5 to instead be displayed in full. eg. \fraction{1}{5}
For this, you only need to change this line:
= \py{pressure_ab}
into this line:
= \py{latex(pressure_ab)}
Because we want pythontex to use the sympy latex printer, instead of the ascii printer.
To summarize, the changes between the original code and the modified code can be viewed here.
All the code in this post is also available in this repo.

XOR statement in PuLP python

I am working on PuLP in python and I want to model the following statement :
x is positive XOR y is positive, where x ans y are integer.
How can I convert this in PuLP code ?
I started with
XOR
I agree with #kabdulla. Binary variables would be the way to go here.
Expanding on that idea a little further: You can use binary variables to indicate whether X is positive (TRUE/1) by a constraint such as M*binary_variable_for_x <= X where M is a sufficiently large number for the problem that would not limit X. Then the binary_variable_for_x can be 1 if X > 0.
Do the same for when Y is positive (TRUE/1).
And then you could write another constraint that requires the sum of these booleans to be >= 1.
binary_variable_for_x + binary_variable_for_y >= 1
There's multiple ways to formulate the problem, but this can be one way.

Trying to end up with two decimal points on a float, but keep getting 0.0

I have a float and would like to limit to just two decimals.
I've tried format(), and round(), and still just get 0, or 0.0
x = 8.972990688205408e-05
print ("x: ", x)
print ("x using round():", round(x))
print ("x using format():"+"{:.2f}".format(x))
output:
x: 8.972990688205408e-05
x using round(): 0
x using format():0.00
I'm expecting 8.98, or 8.97 depending on what method used. What am I missing?
You are using the scientific notation. As glhr pointed out in the comments, you are trying to round 8.972990688205408e-05 = 0.00008972990688205408. This means trying to round as type float will only print the first two 0s after the decimal points, resulting in 0.00. You will have to format via 0:.2e:
x = 8.972990688205408e-05
print("{0:.2e}".format(x))
This prints:
8.97e-05
You asked in one of your comments on how to get only the 8.97.
This is the way to do it:
y = x*1e+05
print("{0:.2f}".format(y))
output:
8.97
In python (and many other programming language), any number suffix with an e with a number, it is power of 10 with the number.
For example
8.9729e05 = 8.9729 x 10^3 = 8972.9
8.9729e-05 = 8.9729 x 10^-3 = 0.000089729
8.9729e0 = 8.9729 x 10^0 = 8.9729
8.972990688205408e-05 8.972990688205408 x 10^-5 = 0.00008972990688205408
8.9729e # invalid syntax
As pointed out by other answer, if you want to print out the exponential round up, you need to use the correct Python string format, you have many choices to choose from. i.e.
e Floating point exponential format (lowercase, precision default to 6 digit)
e Floating point exponential format (uppercase, precision default to 6 digit).
g Same as "e" if exponent is greater than -4 or less than precision, "f" otherwise
G Same as "E" if exponent is greater than -4 or less than precision, "F" otherwise
e.g.
x = 8.972990688205408e-05
print('{:e}'.format(x)) # 8.972991e-05
print('{:E}'.format(x)) # 8.972991E-05
print('{:.2e}'.format(x)) # 8.97e-05
(Update)
OP asked a way to remove the exponent "E" number. Since str.format() or "%" notation just output a string object, break the "e" notation out of the string will do the trick.
'{:.2e}'.format(x).split("e") # ['8.97', '-05']
print('{:.2e}'.format(x).split('e')[0]) # 8.97
If I understand correctly, you only want to round the mantissa/significand? If you want to keep x as a float and output a float, just specify the precision when calling round:
x = round(8.972990688205408e-05,7)
Output:
8.97e-05
However, I recommend converting x with the decimal module first, which "provides support for fast correctly-rounded decimal floating point arithmetic" (see this answer):
from decimal import Decimal
x = Decimal('8.972990688205408e-05').quantize(Decimal('1e-7')) # output: 0.0000897
print('%.2E' % x)
Output:
8.97E-05
Or use the short form of the format method, which gives the same output:
print(f"{x:.2E}")
rount() returns closest multiple of 10 to the power minus ndigits,
so there is no chance you will get 8.98 or 8.97. you can check here also.

String Formatting with Decimal Number Python

I am working on building a report. One figure on the report needs to be expressed in millions. I wrote some basic formatters to handle various types of formatting that need to be consistent throughout the report. To do this, I use lambda functions and string formatting. The two functions are below. One is to round, the other to format.
formatter_round = lambda x: 0 if (x is None or x is bool) else round(x/1000000,1)
formatter_dollar = lambda x: '${:,.1}'.format(0) if (x is None or x == 0) else ('${:,.1}'.format(x) if x >= 0 else '$({:,.1})'.format(abs(x)))
Now comes the problem. See my example below.
I am dealing with two numbers, a = 350000 and b = 850000.
For a everything works as I'd expect. The float isn't necessarily correct when rounded ( not "what I'd expect", but understandable behavior), but the decimal is correct.
a = 350000
formatter_dollar(formatter_round(a))
Out[89]: '$0.3'
a = Decimal(a)
formatter_dollar(formatter_round(a))
Out[91]: '$0.4'
When I run the same example with b, however, this breaks down.
b = 850000
formatter_dollar(formatter_round(b))
Out[93]: '$0.8'
b = Decimal(b)
formatter_dollar(formatter_round(b))
Out[95]: '$0.8'
My question is, how can I properly round and display numbers?
I thought my issue was floating point numbers, and a seemed to confirm that. Then when I ran the same with b, I realized that isn't the case.

math.sqrt function python gives same result for two different values [duplicate]

Why does the math module return the wrong result?
First test
A = 12345678917
print 'A =',A
B = sqrt(A**2)
print 'B =',int(B)
Result
A = 12345678917
B = 12345678917
Here, the result is correct.
Second test
A = 123456758365483459347856
print 'A =',A
B = sqrt(A**2)
print 'B =',int(B)
Result
A = 123456758365483459347856
B = 123456758365483467538432
Here the result is incorrect.
Why is that the case?
Because math.sqrt(..) first casts the number to a floating point and floating points have a limited mantissa: it can only represent part of the number correctly. So float(A**2) is not equal to A**2. Next it calculates the math.sqrt which is also approximately correct.
Most functions working with floating points will never be fully correct to their integer counterparts. Floating point calculations are almost inherently approximative.
If one calculates A**2 one gets:
>>> 12345678917**2
152415787921658292889L
Now if one converts it to a float(..), one gets:
>>> float(12345678917**2)
1.5241578792165828e+20
But if you now ask whether the two are equal:
>>> float(12345678917**2) == 12345678917**2
False
So information has been lost while converting it to a float.
You can read more about how floats work and why these are approximative in the Wikipedia article about IEEE-754, the formal definition on how floating points work.
The documentation for the math module states "It provides access to the mathematical functions defined by the C standard." It also states "Except when explicitly noted otherwise, all return values are floats."
Those together mean that the parameter to the square root function is a float value. In most systems that means a floating point value that fits into 8 bytes, which is called "double" in the C language. Your code converts your integer value into such a value before calculating the square root, then returns such a value.
However, the 8-byte floating point value can store at most 15 to 17 significant decimal digits. That is what you are getting in your results.
If you want better precision in your square roots, use a function that is guaranteed to give full precision for an integer argument. Just do a web search and you will find several. Those usually do a variation of the Newton-Raphson method to iterate and eventually end at the correct answer. Be aware that this is significantly slower that the math module's sqrt function.
Here is a routine that I modified from the internet. I can't cite the source right now. This version also works for non-integer arguments but just returns the integer part of the square root.
def isqrt(x):
"""Return the integer part of the square root of x, even for very
large values."""
if x < 0:
raise ValueError('square root not defined for negative numbers')
n = int(x)
if n == 0:
return 0
a, b = divmod(n.bit_length(), 2)
x = (1 << (a+b)) - 1
while True:
y = (x + n//x) // 2
if y >= x:
return x
x = y
If you want to calculate sqrt of really large numbers and you need exact results, you can use sympy:
import sympy
num = sympy.Integer(123456758365483459347856)
print(int(num) == int(sympy.sqrt(num**2)))
The way floating-point numbers are stored in memory makes calculations with them prone to slight errors that can nevertheless be significant when exact results are needed. As mentioned in one of the comments, the decimal library can help you here:
>>> A = Decimal(12345678917)
>>> A
Decimal('123456758365483459347856')
>>> B = A.sqrt()**2
>>> B
Decimal('123456758365483459347856.0000')
>>> A == B
True
>>> int(B)
123456758365483459347856
I use version 3.6, which has no hardcoded limit on the size of integers. I don't know if, in 2.7, casting B as an int would cause overflow, but decimal is incredibly useful regardless.

Resources