Add pythagorean query

This commit is contained in:
Rasmus Lerchedahl Petersen
2019-05-01 13:16:40 +02:00
parent 1203c7305a
commit b5b2d56bfa
6 changed files with 111 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
# We know that a^2 + b^2 = c^2, and wish to use this to compute c
from math import sqrt, hypot
a = 3e154 # a^2 > 1e308
b = 4e154 # b^2 > 1e308
# with these, c = 5e154 which is less that 1e308
def longSideDirect():
return sqrt(a**2 + b**2) # this will overflow
def longSideBuiltin():
return hypot(a, b) # better to use built-in function

View File

@@ -0,0 +1,33 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Calculating the length of the hypotenuse using the standard formula <code>c = sqrt(a**2 + b**2)</code> may lead to overflow if the two other sides are both very large.
Even though <code>c</code> will not be much bigger than <code>max(a, b)</code>, either <code>a**2</code> or <code>b**2</code> (or both) will.
Thus, the calculation could overflow, even though the result is well within representable range.
</p>
</overview>
<recommendation>
<p>
Rather than <code>sqrt(a**2 + b**2)</code>, use the built-in function <code>hypot(a,b)</code> from the <code>math</code> library.
</p>
</recommendation>
<example>
<p>
The following code shows two different ways of computing the hypotenuse.
The first is a direct rewrite of the Pythagorean theorem, the second uses the built-in function.
</p>
<sample src="Pythagorean.py" />
</example>
<references>
<li>Python Language Reference: <a href="https://docs.python.org/library/math.html#math.hypot">The hypot function</a></li>
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Hypot">Hypot</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,48 @@
/**
* @name Pythagorean calculation with sub-optimal numerics
* @description Calculating the length of the hypotenuse using the standard formula may lead to overflow.
* @kind problem
* @tags accuracy
* @problem.severity warning
* @sub-severity low
* @precision medium
* @id py/pythagorean
*/
import python
predicate squareOp(BinaryExpr e) {
e.getOp() instanceof Pow and e.getRight().(IntegerLiteral).getN() = "2"
}
predicate squareMul(BinaryExpr e) {
e.getOp() instanceof Mult and e.getRight().(Name).getId() = e.getLeft().(Name).getId()
}
predicate squareRef(Name e) {
e.isUse() and
exists(SsaVariable v, Expr s |
v.getVariable() = e.getVariable() |
s = v.getDefinition().getNode().getParentNode().(AssignStmt).getValue() and
square(s)
)
}
predicate square(Expr e) {
squareOp(e)
or
squareMul(e)
or
squareRef(e)
}
from
Call c,
BinaryExpr s
where
c.getFunc().toString() = "sqrt" and
c.getArg(0) = s and
s.getOp() instanceof Add and
square(s.getLeft()) and square(s.getRight())
select
c, "Pythagorean calculation with sub-optimal numerics"

View File

@@ -0,0 +1,3 @@
| pythagorean_test.py:6:12:6:28 | sqrt() | Pythagorean calculation with sub-optimal numerics |
| pythagorean_test.py:9:12:9:26 | sqrt() | Pythagorean calculation with sub-optimal numerics |
| pythagorean_test.py:14:12:14:24 | sqrt() | Pythagorean calculation with sub-optimal numerics |

View File

@@ -0,0 +1 @@
Numerics/Pythagorean.ql

View File

@@ -0,0 +1,14 @@
# Computing the Euclidian norm using the Pythagorean Theorem
from math import sqrt
def withPow(a, b):
return sqrt(a**2 + b**2)
def withMul(a, b):
return sqrt(a*a + b*b)
def withRef(a, b):
a2 = a**2
b2 = b*b
return sqrt(a2 + b2)