Skip to content

Commit 81bf5e7

Browse files
committed
Fix --nan-check false positive for finite/+INFINITY division
- Update NaN check condition to require infinity in nominator - Add regression tests to validate IEEE 754 infinity arithmetic - Support __builtin_isfinite GCC built-in in C front-end Fixes: #8634
1 parent 99345cb commit 81bf5e7

File tree

9 files changed

+120
-12
lines changed

9 files changed

+120
-12
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Test case for bug fix: finite / +INFINITY should NOT trigger --nan-check
2+
// According to IEEE 754-2019 Section 6.1: "division(x, ∞) for finite x"
3+
// should produce 0.0, not NaN. Hence, none of the operations below should
4+
// trigger nan-check assertions.
5+
6+
#include <assert.h>
7+
#include <math.h>
8+
#include <stdint.h>
9+
10+
union float_bits
11+
{
12+
uint32_t u;
13+
float f;
14+
};
15+
16+
int main(void)
17+
{
18+
// Test case 1: Using union to create +INFINITY as mentioned in the bug report
19+
union float_bits a, b;
20+
a.u = 1065353216; // 1.0
21+
b.u = 2139095040; // +INF
22+
23+
// This should produce 0.0, not NaN - should NOT trigger nan-check failure
24+
float result1 = a.f / b.f;
25+
assert(fpclassify(result1) == FP_ZERO && !signbit(result1));
26+
27+
// Test case 2: Direct assignment as mentioned in the bug report
28+
float x = 1.0f;
29+
float y = INFINITY;
30+
float result2 = x / y;
31+
assert(fpclassify(result2) == FP_ZERO && !signbit(result2));
32+
33+
// Test case 3: Negative finite / infinity should also be 0.0 (negative zero)
34+
float neg_x = -1.0f;
35+
float result3 = neg_x / INFINITY;
36+
assert(fpclassify(result3) == FP_ZERO && signbit(result3));
37+
38+
// Test case 4: Various finite values divided by infinity
39+
float nd_positive = __VERIFIER_nondet_float();
40+
__CPROVER_assume(isfinite(nd_positive) && nd_positive > 0);
41+
float result4 = nd_positive / INFINITY;
42+
assert(fpclassify(result4) == FP_ZERO && !signbit(result4));
43+
float nd_negative = __VERIFIER_nondet_float();
44+
__CPROVER_assume(isfinite(nd_negative) && nd_negative < 0);
45+
float result5 = nd_negative / INFINITY;
46+
assert(fpclassify(result5) == FP_ZERO && signbit(result5));
47+
48+
return 0;
49+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE no-new-smt
2+
main.c
3+
--nan-check
4+
^VERIFICATION SUCCESSFUL$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This test verifies that dividing a finite float by +INFINITY does NOT
10+
incorrectly trigger a NaN check failure (bug fix). According to IEEE 754-2019
11+
Section 6.1, division(x, ∞) for finite x = 0.0. Only inf/inf should produce NaN
12+
(which is covered in test float-inf-div-inf)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Test case for bug fix: inf / inf SHOULD trigger --nan-check failure
2+
// According to IEEE 754-2019, inf/inf produces NaN
3+
4+
#include <assert.h>
5+
#include <math.h>
6+
7+
int main(void)
8+
{
9+
// Ensure infinity / infinity produces NaN and triggers nan-check
10+
float inf1 = INFINITY;
11+
float inf2 = INFINITY;
12+
float result1 = inf1 / inf2; // Should trigger NaN check
13+
assert(isnan(result1));
14+
15+
// Also test -inf / inf
16+
float result2 = (-INFINITY) / INFINITY; // Should trigger NaN check
17+
assert(isnan(result2));
18+
19+
// And inf / -inf
20+
float result3 = INFINITY / (-INFINITY); // Should trigger NaN check
21+
assert(isnan(result3));
22+
23+
// And -inf / -inf
24+
float result4 = (-INFINITY) / (-INFINITY); // Should trigger NaN check
25+
assert(isnan(result4));
26+
27+
return 0;
28+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
main.c
3+
--nan-check
4+
\[main.NaN.1\] line \d+ NaN on / in inf1 / inf2: FAILURE
5+
\[main.NaN.2\] line \d+ NaN on / in -(\+INFINITY|return_value___builtin_(huge_val|inf)f\$\d+) / (\+INFINITY|return_value___builtin_(huge_val|inf)f\$\d+): FAILURE
6+
\[main.NaN.3\] line \d+ NaN on / in (\+INFINITY|return_value___builtin_(huge_val|inf)f\$\d+) / -(\+INFINITY|return_value___builtin_(huge_val|inf)f\$\d+): FAILURE
7+
\[main.NaN.4\] line \d+ NaN on / in -(\+INFINITY|return_value___builtin_(huge_val|inf)f\$\d+) / -(\+INFINITY|return_value___builtin_(huge_val|inf)f\$\d+): FAILURE
8+
^\*\* 4 of \d+ failed
9+
^VERIFICATION FAILED$
10+
^EXIT=10$
11+
^SIGNAL=0$
12+
--
13+
--
14+
This test verifies that inf/inf correctly triggers NaN check failures.
15+
According to IEEE 754-2019, inf/inf = NaN.

regression/cbmc/float-nan-check/main.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ int main(void)
3939
// these operations should generate assertions
4040
// 0.0 / 0.0 = NaN
4141
f = myzero / myzero;
42-
// n / Inf = NaN
43-
f = n / (myinf);
42+
// Inf / Inf = NaN (per IEEE 754-2019 Section 6.1)
43+
f = myinf / myinf;
4444
// Inf * 0 = NaN
4545
f = (myinf)*myzero;
4646
// -Inf + Inf = NaN

regression/cbmc/float-nan-check/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ main.c
33
--nan-check
44
\[main.NaN.1\] line \d+ NaN on \+ in byte_extract_(big|little)_endian\(c, (0|0l|0ll), float\) \+ myzero: SUCCESS
55
\[main.NaN.2\] line \d+ NaN on / in myzero / myzero: FAILURE
6-
\[main.NaN.3\] line \d+ NaN on / in \(float\)n / myinf: FAILURE
6+
\[main.NaN.3\] line \d+ NaN on / in myinf / myinf: FAILURE
77
\[main.NaN.4\] line \d+ NaN on \* in myinf \* myzero: FAILURE
88
\[main.NaN.5\] line \d+ NaN on \+ in -myinf \+ myinf: FAILURE
99
\[main.NaN.6\] line \d+ NaN on \+ in myinf \+ -myinf: FAILURE

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3247,9 +3247,11 @@ exprt c_typecheck_baset::do_special_functions(
32473247

32483248
return typecast_exprt::conditional_cast(isnan_expr, expr.type());
32493249
}
3250-
else if(identifier==CPROVER_PREFIX "isfinitef" ||
3251-
identifier==CPROVER_PREFIX "isfinited" ||
3252-
identifier==CPROVER_PREFIX "isfiniteld")
3250+
else if(
3251+
identifier == CPROVER_PREFIX "isfinitef" ||
3252+
identifier == CPROVER_PREFIX "isfinited" ||
3253+
identifier == CPROVER_PREFIX "isfiniteld" ||
3254+
identifier == "__builtin_isfinite")
32533255
{
32543256
if(expr.arguments().size()!=1)
32553257
{

src/ansi-c/goto-conversion/goto_check_c.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,18 +1254,20 @@ void goto_check_ct::nan_check(const exprt &expr, const guardt &guard)
12541254
{
12551255
const auto &div_expr = to_div_expr(expr);
12561256

1257-
// there a two ways to get a new NaN on division:
1258-
// 0/0 = NaN and x/inf = NaN
1257+
// there are two ways to get a new NaN on division:
1258+
// 0/0 = NaN and inf/inf = NaN
12591259
// (note that x/0 = +-inf for x!=0 and x!=inf)
1260-
const and_exprt zero_div_zero(
1260+
// (note that finite/inf = +-0.0, NOT NaN per IEEE 754-2019 Section 6.1)
1261+
and_exprt zero_div_zero(
12611262
ieee_float_equal_exprt(
12621263
div_expr.op0(), from_integer(0, div_expr.dividend().type())),
12631264
ieee_float_equal_exprt(
12641265
div_expr.op1(), from_integer(0, div_expr.divisor().type())));
12651266

1266-
const isinf_exprt div_inf(div_expr.op1());
1267+
and_exprt inf_div_inf{
1268+
isinf_exprt{div_expr.op0()}, isinf_exprt{div_expr.op1()}};
12671269

1268-
isnan = or_exprt(zero_div_zero, div_inf);
1270+
isnan = or_exprt{std::move(zero_div_zero), std::move(inf_div_inf)};
12691271
}
12701272
else if(expr.id() == ID_mult)
12711273
{

src/util/ieee_float.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,7 @@ ieee_floatt &ieee_floatt::operator/=(const ieee_floatt &other)
10511051
return *this;
10521052
}
10531053

1054-
// x/inf = NaN
1054+
// inf/inf = NaN, x/inf = 0 for finite x
10551055
if(other.infinity_flag)
10561056
{
10571057
if(infinity_flag)

0 commit comments

Comments
 (0)