Skip to content

Commit 252c1ce

Browse files
authored
Audit for rintf* and nearbyintf* intrinsics (rust-lang#1277)
* Restore `rintf*` intrinsics * Remove negative tests * Restore `nearbyintf*` intrinsics * Remove negative tests * Add tests for `rintf*` and `nearbyintf*` * Require `diff` to be [0, 0.5]
1 parent 887c2e3 commit 252c1ce

File tree

11 files changed

+412
-66
lines changed

11 files changed

+412
-66
lines changed

kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs

+4-12
Original file line numberDiff line numberDiff line change
@@ -549,12 +549,8 @@ impl<'tcx> GotocCtx<'tcx> {
549549
"minnumf32" => codegen_simple_intrinsic!(Fminf),
550550
"minnumf64" => codegen_simple_intrinsic!(Fmin),
551551
"mul_with_overflow" => codegen_op_with_overflow!(mul_overflow),
552-
"nearbyintf32" => codegen_unimplemented_intrinsic!(
553-
"https://github.com/model-checking/kani/issues/1025"
554-
),
555-
"nearbyintf64" => codegen_unimplemented_intrinsic!(
556-
"https://github.com/model-checking/kani/issues/1025"
557-
),
552+
"nearbyintf32" => codegen_simple_intrinsic!(Nearbyintf),
553+
"nearbyintf64" => codegen_simple_intrinsic!(Nearbyint),
558554
"needs_drop" => codegen_intrinsic_const!(),
559555
"offset" => self.codegen_offset(intrinsic, instance, fargs, p, loc),
560556
"powf32" => unstable_codegen!(codegen_simple_intrinsic!(Powf)),
@@ -567,12 +563,8 @@ impl<'tcx> GotocCtx<'tcx> {
567563
"ptr_offset_from" => self.codegen_ptr_offset_from(fargs, p, loc),
568564
"ptr_offset_from_unsigned" => self.codegen_ptr_offset_from_unsigned(fargs, p, loc),
569565
"raw_eq" => self.codegen_intrinsic_raw_eq(instance, fargs, p, loc),
570-
"rintf32" => codegen_unimplemented_intrinsic!(
571-
"https://github.com/model-checking/kani/issues/1025"
572-
),
573-
"rintf64" => codegen_unimplemented_intrinsic!(
574-
"https://github.com/model-checking/kani/issues/1025"
575-
),
566+
"rintf32" => codegen_simple_intrinsic!(Rintf),
567+
"rintf64" => codegen_simple_intrinsic!(Rint),
576568
"rotate_left" => codegen_intrinsic_binop!(rol),
577569
"rotate_right" => codegen_intrinsic_binop!(ror),
578570
"roundf32" => codegen_simple_intrinsic!(Roundf),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that `nearbyintf32` returns the nearest integer to the argument. The
5+
// default rounding mode is rounding half to even, which is described here:
6+
// https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
7+
#![feature(core_intrinsics)]
8+
use std::intrinsics::nearbyintf32;
9+
10+
#[kani::proof]
11+
fn test_one() {
12+
let one = 1.0;
13+
let result = unsafe { nearbyintf32(one) };
14+
assert!(result == 1.0);
15+
}
16+
17+
#[kani::proof]
18+
fn test_one_frac() {
19+
let one_frac = 1.9;
20+
let result = unsafe { nearbyintf32(one_frac) };
21+
assert!(result == 2.0);
22+
}
23+
24+
#[kani::proof]
25+
fn test_half_down() {
26+
let one_frac = 2.5;
27+
let result = unsafe { nearbyintf32(one_frac) };
28+
assert!(result == 2.0);
29+
}
30+
31+
#[kani::proof]
32+
fn test_half_up() {
33+
let one_frac = 3.5;
34+
let result = unsafe { nearbyintf32(one_frac) };
35+
assert!(result == 4.0);
36+
}
37+
38+
#[kani::proof]
39+
fn test_conc() {
40+
let conc = -42.6;
41+
let result = unsafe { nearbyintf32(conc) };
42+
assert!(result == -43.0);
43+
}
44+
45+
#[kani::proof]
46+
fn test_conc_sci() {
47+
let conc = 5.4e-2;
48+
let result = unsafe { nearbyintf32(conc) };
49+
assert!(result == 0.0);
50+
}
51+
52+
#[kani::proof]
53+
fn test_towards_nearest() {
54+
let x: f32 = kani::any();
55+
kani::assume(!x.is_nan());
56+
kani::assume(!x.is_infinite());
57+
let result = unsafe { nearbyintf32(x) };
58+
let frac = x.fract().abs();
59+
if x.is_sign_positive() {
60+
if frac > 0.5 {
61+
assert!(result > x);
62+
} else if frac < 0.5 {
63+
assert!(result <= x);
64+
} else {
65+
// This would fail if conversion checks were on
66+
let integer = x as i64;
67+
if integer % 2 == 0 {
68+
assert!(result < x);
69+
} else {
70+
assert!(result > x);
71+
}
72+
}
73+
} else {
74+
if frac > 0.5 {
75+
assert!(result < x);
76+
} else if frac < 0.5 {
77+
assert!(result >= x);
78+
} else {
79+
// This would fail if conversion checks were on
80+
let integer = x as i64;
81+
if integer % 2 == 0 {
82+
assert!(result > x);
83+
} else {
84+
assert!(result < x);
85+
}
86+
}
87+
}
88+
}
89+
90+
#[kani::proof]
91+
fn test_diff_half_one() {
92+
let x: f32 = kani::any();
93+
kani::assume(!x.is_nan());
94+
kani::assume(!x.is_infinite());
95+
let result = unsafe { nearbyintf32(x) };
96+
let diff = (x - result).abs();
97+
assert!(diff <= 0.5);
98+
assert!(diff >= 0.0);
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that `nearbyintf64` returns the nearest integer to the argument. The
5+
// default rounding mode is rounding half to even, which is described here:
6+
// https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
7+
#![feature(core_intrinsics)]
8+
use std::intrinsics::nearbyintf64;
9+
10+
#[kani::proof]
11+
fn test_one() {
12+
let one = 1.0;
13+
let result = unsafe { nearbyintf64(one) };
14+
assert!(result == 1.0);
15+
}
16+
17+
#[kani::proof]
18+
fn test_one_frac() {
19+
let one_frac = 1.9;
20+
let result = unsafe { nearbyintf64(one_frac) };
21+
assert!(result == 2.0);
22+
}
23+
24+
#[kani::proof]
25+
fn test_half_down() {
26+
let one_frac = 2.5;
27+
let result = unsafe { nearbyintf64(one_frac) };
28+
assert!(result == 2.0);
29+
}
30+
31+
#[kani::proof]
32+
fn test_half_up() {
33+
let one_frac = 3.5;
34+
let result = unsafe { nearbyintf64(one_frac) };
35+
assert!(result == 4.0);
36+
}
37+
38+
#[kani::proof]
39+
fn test_conc() {
40+
let conc = -42.6;
41+
let result = unsafe { nearbyintf64(conc) };
42+
assert!(result == -43.0);
43+
}
44+
45+
#[kani::proof]
46+
fn test_conc_sci() {
47+
let conc = 5.4e-2;
48+
let result = unsafe { nearbyintf64(conc) };
49+
assert!(result == 0.0);
50+
}
51+
52+
#[kani::proof]
53+
fn test_towards_nearest() {
54+
let x: f64 = kani::any();
55+
kani::assume(!x.is_nan());
56+
kani::assume(!x.is_infinite());
57+
let result = unsafe { nearbyintf64(x) };
58+
let frac = x.fract().abs();
59+
if x.is_sign_positive() {
60+
if frac > 0.5 {
61+
assert!(result > x);
62+
} else if frac < 0.5 {
63+
assert!(result <= x);
64+
} else {
65+
// This would fail if conversion checks were on
66+
let integer = x as i64;
67+
if integer % 2 == 0 {
68+
assert!(result < x);
69+
} else {
70+
assert!(result > x);
71+
}
72+
}
73+
} else {
74+
if frac > 0.5 {
75+
assert!(result < x);
76+
} else if frac < 0.5 {
77+
assert!(result >= x);
78+
} else {
79+
// This would fail if conversion checks were on
80+
let integer = x as i64;
81+
if integer % 2 == 0 {
82+
assert!(result > x);
83+
} else {
84+
assert!(result < x);
85+
}
86+
}
87+
}
88+
}
89+
90+
#[kani::proof]
91+
fn test_diff_half_one() {
92+
let x: f64 = kani::any();
93+
kani::assume(!x.is_nan());
94+
kani::assume(!x.is_infinite());
95+
let result = unsafe { nearbyintf64(x) };
96+
let diff = (x - result).abs();
97+
assert!(diff <= 0.5);
98+
assert!(diff >= 0.0);
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that `rintf32` returns the nearest integer to the argument. The
5+
// default rounding mode is rounding half to even, which is described here:
6+
// https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
7+
//
8+
// `rintf32` works like `nearbyintf32`, but it may raise an inexact
9+
// floating-point exception which isn't supported in Rust:
10+
// https://github.com/rust-lang/rust/issues/10186
11+
// So in practice, `rintf32` and `nearbyintf32` work the same way.
12+
#![feature(core_intrinsics)]
13+
use std::intrinsics::rintf32;
14+
15+
#[kani::proof]
16+
fn test_one() {
17+
let one = 1.0;
18+
let result = unsafe { rintf32(one) };
19+
assert!(result == 1.0);
20+
}
21+
22+
#[kani::proof]
23+
fn test_one_frac() {
24+
let one_frac = 1.9;
25+
let result = unsafe { rintf32(one_frac) };
26+
assert!(result == 2.0);
27+
}
28+
29+
#[kani::proof]
30+
fn test_half_down() {
31+
let one_frac = 2.5;
32+
let result = unsafe { rintf32(one_frac) };
33+
assert!(result == 2.0);
34+
}
35+
36+
#[kani::proof]
37+
fn test_half_up() {
38+
let one_frac = 3.5;
39+
let result = unsafe { rintf32(one_frac) };
40+
assert!(result == 4.0);
41+
}
42+
43+
#[kani::proof]
44+
fn test_conc() {
45+
let conc = -42.6;
46+
let result = unsafe { rintf32(conc) };
47+
assert!(result == -43.0);
48+
}
49+
50+
#[kani::proof]
51+
fn test_conc_sci() {
52+
let conc = 5.4e-2;
53+
let result = unsafe { rintf32(conc) };
54+
assert!(result == 0.0);
55+
}
56+
57+
#[kani::proof]
58+
fn test_towards_nearest() {
59+
let x: f32 = kani::any();
60+
kani::assume(!x.is_nan());
61+
kani::assume(!x.is_infinite());
62+
let result = unsafe { rintf32(x) };
63+
let frac = x.fract().abs();
64+
if x.is_sign_positive() {
65+
if frac > 0.5 {
66+
assert!(result > x);
67+
} else if frac < 0.5 {
68+
assert!(result <= x);
69+
} else {
70+
// This would fail if conversion checks were on
71+
let integer = x as i64;
72+
if integer % 2 == 0 {
73+
assert!(result < x);
74+
} else {
75+
assert!(result > x);
76+
}
77+
}
78+
} else {
79+
if frac > 0.5 {
80+
assert!(result < x);
81+
} else if frac < 0.5 {
82+
assert!(result >= x);
83+
} else {
84+
// This would fail if conversion checks were on
85+
let integer = x as i64;
86+
if integer % 2 == 0 {
87+
assert!(result > x);
88+
} else {
89+
assert!(result < x);
90+
}
91+
}
92+
}
93+
}
94+
95+
#[kani::proof]
96+
fn test_diff_half_one() {
97+
let x: f32 = kani::any();
98+
kani::assume(!x.is_nan());
99+
kani::assume(!x.is_infinite());
100+
let result = unsafe { rintf32(x) };
101+
let diff = (x - result).abs();
102+
assert!(diff <= 0.5);
103+
assert!(diff >= 0.0);
104+
}

0 commit comments

Comments
 (0)