FA-16841 / Floating-point arithmetic / Open access
Atan2 ignores the x sign for a doubly infinite direction · case 01
Atan2 ignores the x sign for a doubly infinite direction.
ROOT CAUSE
Atan2 ignores the x sign for a doubly infinite direction. The faulty expression is angle=math.pi/4.
VERIFIED REPAIR
Apply the contract at this fault site using angle=math.pi/4 if x>0 else 3*math.pi/4.
Unsuccessful approach: The attempted local correction angle=3*math.pi/4 still violates the explicit regression fixtures.
Case contract
Evaluate atan2(y,x) including signed zero and infinite directions; NaN produces nan. Finite nonzero coordinates use ratio of smaller magnitude and quadrant reconstruction. Finite results are rendered to eleven significant decimal digits; modeled domain violations and arithmetic errors are explicit strings.
Why this case matters
An offline floating representation model isolates a reproducible arithmetic fault.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
import struct
def render(x):
if math.isnan(x): return 'nan'
if math.isinf(x): return '-infinity' if x<0 else '+infinity'
return format(x,'.11g')
N = 1
observations = []
def solve(y,x):
try:
if math.isnan(x) or math.isnan(y): return 'nan'
if math.isinf(x) and math.isinf(y):
angle=math.pi/4
return render(math.copysign(angle,y))
if y==0:
return render(math.copysign(math.pi if math.copysign(1.0,x)<0 else 0.0,y))
if x==0: return render(math.copysign(math.pi/2,y))
a=abs(x); b=abs(y)
if b<=a: angle=math.atan(b/a)
else: angle=math.pi/2-math.atan(a/b)
if x<0: angle=math.pi-angle
return render(math.copysign(angle,y))
except (ValueError, OverflowError, ZeroDivisionError, TypeError):
return "arithmetic-error"
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('positive zero axis', solve(0.0,float(N)), "0")
check('negative zero axis', solve(-0.0,float(N)), "-0")
check('branch above', solve(0.0,-float(N)), render(math.pi))
check('branch below', solve(-0.0,-float(N)), render(-math.pi))
check('positive origin', solve(0.0,0.0), "0")
check('positive vertical', solve(float(N),0.0), render(math.pi/2))
check('steep slope', solve(10.0*N,1.0), render(math.atan2(10*N,1)))
check('signed origin', solve(0.0,-0.0), render(math.pi))
check('vertical negative', solve(-float(N),0.0), render(-math.pi/2))
check('quadrant one', solve(float(N),2.0), render(math.atan2(N,2)))
check('quadrant two', solve(float(N),-2.0), render(math.atan2(N,-2)))
check('quadrant three', solve(-float(N),-2.0), render(math.atan2(-N,-2)))
check('tiny slope', solve(N*1e-300,1e300), "0")
check('both infinite', solve(math.inf,math.inf), render(math.pi/4))
check('negative infinite', solve(-math.inf,-math.inf), render(-3*math.pi/4))
check('nan coordinate', solve(float("nan"),float(N)), "nan")
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| positive zero axis | 0 | 0 | Passed |
| negative zero axis | -0 | -0 | Passed |
| branch above | 3.1415926536 | 3.1415926536 | Passed |
| branch below | -3.1415926536 | -3.1415926536 | Passed |
| positive origin | 0 | 0 | Passed |
| positive vertical | 1.5707963268 | 1.5707963268 | Passed |
| steep slope | 1.4711276743 | 1.4711276743 | Passed |
| signed origin | 3.1415926536 | 3.1415926536 | Passed |
| vertical negative | -1.5707963268 | -1.5707963268 | Passed |
| quadrant one | 0.463647609 | 0.463647609 | Passed |
| quadrant two | 2.6779450446 | 2.6779450446 | Passed |
| quadrant three | -2.6779450446 | -2.6779450446 | Passed |
| tiny slope | 0 | 0 | Passed |
| both infinite | 0.7853981634 | 0.7853981634 | Passed |
| negative infinite | -0.7853981634 | -2.3561944902 | Failed |
| nan coordinate | nan | nan | Passed |
SHA-256 / baea5b564a355c6fb719e0af3b43b001d386b18a2e199c29bdac16aad5857b37
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
import struct
def render(x):
if math.isnan(x): return 'nan'
if math.isinf(x): return '-infinity' if x<0 else '+infinity'
return format(x,'.11g')
N = 1
observations = []
def solve(y,x):
try:
if math.isnan(x) or math.isnan(y): return 'nan'
if math.isinf(x) and math.isinf(y):
angle=3*math.pi/4
return render(math.copysign(angle,y))
if y==0:
return render(math.copysign(math.pi if math.copysign(1.0,x)<0 else 0.0,y))
if x==0: return render(math.copysign(math.pi/2,y))
a=abs(x); b=abs(y)
if b<=a: angle=math.atan(b/a)
else: angle=math.pi/2-math.atan(a/b)
if x<0: angle=math.pi-angle
return render(math.copysign(angle,y))
except (ValueError, OverflowError, ZeroDivisionError, TypeError):
return "arithmetic-error"
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('positive zero axis', solve(0.0,float(N)), "0")
check('negative zero axis', solve(-0.0,float(N)), "-0")
check('branch above', solve(0.0,-float(N)), render(math.pi))
check('branch below', solve(-0.0,-float(N)), render(-math.pi))
check('positive origin', solve(0.0,0.0), "0")
check('positive vertical', solve(float(N),0.0), render(math.pi/2))
check('steep slope', solve(10.0*N,1.0), render(math.atan2(10*N,1)))
check('signed origin', solve(0.0,-0.0), render(math.pi))
check('vertical negative', solve(-float(N),0.0), render(-math.pi/2))
check('quadrant one', solve(float(N),2.0), render(math.atan2(N,2)))
check('quadrant two', solve(float(N),-2.0), render(math.atan2(N,-2)))
check('quadrant three', solve(-float(N),-2.0), render(math.atan2(-N,-2)))
check('tiny slope', solve(N*1e-300,1e300), "0")
check('both infinite', solve(math.inf,math.inf), render(math.pi/4))
check('negative infinite', solve(-math.inf,-math.inf), render(-3*math.pi/4))
check('nan coordinate', solve(float("nan"),float(N)), "nan")
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| positive zero axis | 0 | 0 | Passed |
| negative zero axis | -0 | -0 | Passed |
| branch above | 3.1415926536 | 3.1415926536 | Passed |
| branch below | -3.1415926536 | -3.1415926536 | Passed |
| positive origin | 0 | 0 | Passed |
| positive vertical | 1.5707963268 | 1.5707963268 | Passed |
| steep slope | 1.4711276743 | 1.4711276743 | Passed |
| signed origin | 3.1415926536 | 3.1415926536 | Passed |
| vertical negative | -1.5707963268 | -1.5707963268 | Passed |
| quadrant one | 0.463647609 | 0.463647609 | Passed |
| quadrant two | 2.6779450446 | 2.6779450446 | Passed |
| quadrant three | -2.6779450446 | -2.6779450446 | Passed |
| tiny slope | 0 | 0 | Passed |
| both infinite | 2.3561944902 | 0.7853981634 | Failed |
| negative infinite | -2.3561944902 | -2.3561944902 | Passed |
| nan coordinate | nan | nan | Passed |
SHA-256 / 430d7d473f8e8de6695f4ed02bc42feeeb673a4dcce77b5d462eb6d34f271f8b
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
import struct
def render(x):
if math.isnan(x): return 'nan'
if math.isinf(x): return '-infinity' if x<0 else '+infinity'
return format(x,'.11g')
N = 1
observations = []
def solve(y,x):
try:
if math.isnan(x) or math.isnan(y): return 'nan'
if math.isinf(x) and math.isinf(y):
angle=math.pi/4 if x>0 else 3*math.pi/4
return render(math.copysign(angle,y))
if y==0:
return render(math.copysign(math.pi if math.copysign(1.0,x)<0 else 0.0,y))
if x==0: return render(math.copysign(math.pi/2,y))
a=abs(x); b=abs(y)
if b<=a: angle=math.atan(b/a)
else: angle=math.pi/2-math.atan(a/b)
if x<0: angle=math.pi-angle
return render(math.copysign(angle,y))
except (ValueError, OverflowError, ZeroDivisionError, TypeError):
return "arithmetic-error"
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('positive zero axis', solve(0.0,float(N)), "0")
check('negative zero axis', solve(-0.0,float(N)), "-0")
check('branch above', solve(0.0,-float(N)), render(math.pi))
check('branch below', solve(-0.0,-float(N)), render(-math.pi))
check('positive origin', solve(0.0,0.0), "0")
check('positive vertical', solve(float(N),0.0), render(math.pi/2))
check('steep slope', solve(10.0*N,1.0), render(math.atan2(10*N,1)))
check('signed origin', solve(0.0,-0.0), render(math.pi))
check('vertical negative', solve(-float(N),0.0), render(-math.pi/2))
check('quadrant one', solve(float(N),2.0), render(math.atan2(N,2)))
check('quadrant two', solve(float(N),-2.0), render(math.atan2(N,-2)))
check('quadrant three', solve(-float(N),-2.0), render(math.atan2(-N,-2)))
check('tiny slope', solve(N*1e-300,1e300), "0")
check('both infinite', solve(math.inf,math.inf), render(math.pi/4))
check('negative infinite', solve(-math.inf,-math.inf), render(-3*math.pi/4))
check('nan coordinate', solve(float("nan"),float(N)), "nan")
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| positive zero axis | 0 | 0 | Passed |
| negative zero axis | -0 | -0 | Passed |
| branch above | 3.1415926536 | 3.1415926536 | Passed |
| branch below | -3.1415926536 | -3.1415926536 | Passed |
| positive origin | 0 | 0 | Passed |
| positive vertical | 1.5707963268 | 1.5707963268 | Passed |
| steep slope | 1.4711276743 | 1.4711276743 | Passed |
| signed origin | 3.1415926536 | 3.1415926536 | Passed |
| vertical negative | -1.5707963268 | -1.5707963268 | Passed |
| quadrant one | 0.463647609 | 0.463647609 | Passed |
| quadrant two | 2.6779450446 | 2.6779450446 | Passed |
| quadrant three | -2.6779450446 | -2.6779450446 | Passed |
| tiny slope | 0 | 0 | Passed |
| both infinite | 0.7853981634 | 0.7853981634 | Passed |
| negative infinite | -2.3561944902 | -2.3561944902 | Passed |
| nan coordinate | nan | nan | Passed |
SHA-256 / f929e4baf91e56cf334bd90146c05b96a4ac1227a21cea2c3278c0336055dc75
Verification & scope
Controlled binary64 or explicitly stipulated miniature format; no hardware exception flags or platform floating environment are modeled. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:39:40.359874+00:00.
Case digest / fa100a1623cb1bdb2b1dd580ed3f54e747b3a5476fc52b01332c18139c67383b