Skip to content

Commit d1a711e

Browse files
author
Mark Shannon
authored
Merge pull request github#1296 from RasmusSemmle/master
Add pythagorean query
2 parents 6994ba9 + b5b2d56 commit d1a711e

File tree

6 files changed

+111
-0
lines changed

6 files changed

+111
-0
lines changed

python/ql/src/Numerics/Pythagorean.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# We know that a^2 + b^2 = c^2, and wish to use this to compute c
2+
from math import sqrt, hypot
3+
4+
a = 3e154 # a^2 > 1e308
5+
b = 4e154 # b^2 > 1e308
6+
# with these, c = 5e154 which is less that 1e308
7+
8+
def longSideDirect():
9+
return sqrt(a**2 + b**2) # this will overflow
10+
11+
def longSideBuiltin():
12+
return hypot(a, b) # better to use built-in function
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
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.
9+
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.
10+
Thus, the calculation could overflow, even though the result is well within representable range.
11+
</p>
12+
</overview>
13+
14+
<recommendation>
15+
<p>
16+
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.
17+
</p>
18+
</recommendation>
19+
20+
<example>
21+
<p>
22+
The following code shows two different ways of computing the hypotenuse.
23+
The first is a direct rewrite of the Pythagorean theorem, the second uses the built-in function.
24+
</p>
25+
26+
<sample src="Pythagorean.py" />
27+
</example>
28+
29+
<references>
30+
<li>Python Language Reference: <a href="https://docs.python.org/library/math.html#math.hypot">The hypot function</a></li>
31+
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Hypot">Hypot</a>.</li>
32+
</references>
33+
</qhelp>

python/ql/src/Numerics/Pythagorean.ql

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @name Pythagorean calculation with sub-optimal numerics
3+
* @description Calculating the length of the hypotenuse using the standard formula may lead to overflow.
4+
* @kind problem
5+
* @tags accuracy
6+
* @problem.severity warning
7+
* @sub-severity low
8+
* @precision medium
9+
* @id py/pythagorean
10+
*/
11+
12+
import python
13+
14+
predicate squareOp(BinaryExpr e) {
15+
e.getOp() instanceof Pow and e.getRight().(IntegerLiteral).getN() = "2"
16+
}
17+
18+
predicate squareMul(BinaryExpr e) {
19+
e.getOp() instanceof Mult and e.getRight().(Name).getId() = e.getLeft().(Name).getId()
20+
}
21+
22+
predicate squareRef(Name e) {
23+
e.isUse() and
24+
exists(SsaVariable v, Expr s |
25+
v.getVariable() = e.getVariable() |
26+
s = v.getDefinition().getNode().getParentNode().(AssignStmt).getValue() and
27+
square(s)
28+
)
29+
}
30+
31+
predicate square(Expr e) {
32+
squareOp(e)
33+
or
34+
squareMul(e)
35+
or
36+
squareRef(e)
37+
}
38+
39+
from
40+
Call c,
41+
BinaryExpr s
42+
where
43+
c.getFunc().toString() = "sqrt" and
44+
c.getArg(0) = s and
45+
s.getOp() instanceof Add and
46+
square(s.getLeft()) and square(s.getRight())
47+
select
48+
c, "Pythagorean calculation with sub-optimal numerics"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| pythagorean_test.py:6:12:6:28 | sqrt() | Pythagorean calculation with sub-optimal numerics |
2+
| pythagorean_test.py:9:12:9:26 | sqrt() | Pythagorean calculation with sub-optimal numerics |
3+
| pythagorean_test.py:14:12:14:24 | sqrt() | Pythagorean calculation with sub-optimal numerics |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Numerics/Pythagorean.ql
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Computing the Euclidian norm using the Pythagorean Theorem
2+
3+
from math import sqrt
4+
5+
def withPow(a, b):
6+
return sqrt(a**2 + b**2)
7+
8+
def withMul(a, b):
9+
return sqrt(a*a + b*b)
10+
11+
def withRef(a, b):
12+
a2 = a**2
13+
b2 = b*b
14+
return sqrt(a2 + b2)

0 commit comments

Comments
 (0)