1
1
//! A generator that checks a handful of cases near infinities, zeros, asymptotes, and NaNs.
2
2
3
- use libm:: support:: { Float , Int } ;
3
+ use libm:: support:: { CastInto , Float , Int } ;
4
4
5
5
use crate :: domain:: get_domain;
6
6
use crate :: gen:: KnownSize ;
7
7
use crate :: run_cfg:: { check_near_count, check_point_count} ;
8
- use crate :: { CheckCtx , FloatExt , MathOp , test_log} ;
8
+ use crate :: { BaseName , CheckCtx , FloatExt , FloatTy , MathOp , test_log} ;
9
9
10
10
/// Generate a sequence of edge cases, e.g. numbers near zeroes and infiniteis.
11
11
pub trait EdgeCaseInput < Op > {
78
78
( ret. into_iter ( ) , count)
79
79
}
80
80
81
- /// Add `AROUND ` values starting at and including `x` and counting up. Uses the smallest possible
81
+ /// Add `points ` values starting at and including `x` and counting up. Uses the smallest possible
82
82
/// increments (1 ULP).
83
83
fn count_up < F : Float > ( mut x : F , points : u64 , values : & mut Vec < F > ) {
84
84
assert ! ( !x. is_nan( ) ) ;
@@ -91,7 +91,7 @@ fn count_up<F: Float>(mut x: F, points: u64, values: &mut Vec<F>) {
91
91
}
92
92
}
93
93
94
- /// Add `AROUND ` values starting at and including `x` and counting down. Uses the smallest possible
94
+ /// Add `points ` values starting at and including `x` and counting down. Uses the smallest possible
95
95
/// increments (1 ULP).
96
96
fn count_down < F : Float > ( mut x : F , points : u64 , values : & mut Vec < F > ) {
97
97
assert ! ( !x. is_nan( ) ) ;
@@ -107,31 +107,87 @@ fn count_down<F: Float>(mut x: F, points: u64, values: &mut Vec<F>) {
107
107
/// Create a list of values around interesting integer points (min, zero, max).
108
108
pub fn int_edge_cases < I : Int > (
109
109
ctx : & CheckCtx ,
110
- _argnum : usize ,
111
- ) -> ( impl Iterator < Item = I > + Clone , u64 ) {
110
+ argnum : usize ,
111
+ ) -> ( impl Iterator < Item = I > + Clone , u64 )
112
+ where
113
+ i32 : CastInto < I > ,
114
+ {
112
115
let mut values = Vec :: new ( ) ;
113
116
let near_points = check_near_count ( ctx) ;
114
117
115
- for up_from in [ I :: MIN , I :: ZERO ] {
116
- let mut x = up_from;
117
- for _ in 0 ..near_points {
118
- values. push ( x) ;
119
- x += I :: ONE ;
120
- }
121
- }
122
-
123
- for down_from in [ I :: ZERO , I :: MAX ] {
124
- let mut x = down_from;
125
- for _ in 0 ..near_points {
126
- values. push ( x) ;
127
- x -= I :: ONE ;
128
- }
118
+ // Check around max/min and zero
119
+ int_count_around ( I :: MIN , near_points, & mut values) ;
120
+ int_count_around ( I :: MAX , near_points, & mut values) ;
121
+ int_count_around ( I :: ZERO , near_points, & mut values) ;
122
+ int_count_around ( I :: ZERO , near_points, & mut values) ;
123
+
124
+ if matches ! ( ctx. base_name, BaseName :: Scalbn | BaseName :: Ldexp ) {
125
+ assert_eq ! ( argnum, 1 , "scalbn integer argument should be arg1" ) ;
126
+ let ( emax, emin, emin_sn) = match ctx. fn_ident . math_op ( ) . float_ty {
127
+ FloatTy :: F16 => {
128
+ #[ cfg( not( f16_enabled) ) ]
129
+ unreachable ! ( ) ;
130
+ #[ cfg( f16_enabled) ]
131
+ ( f16:: EXP_MAX , f16:: EXP_MIN , f16:: EXP_MIN_SUBNORM )
132
+ }
133
+ FloatTy :: F32 => ( f32:: EXP_MAX , f32:: EXP_MIN , f32:: EXP_MIN_SUBNORM ) ,
134
+ FloatTy :: F64 => ( f64:: EXP_MAX , f64:: EXP_MIN , f64:: EXP_MIN_SUBNORM ) ,
135
+ FloatTy :: F128 => {
136
+ #[ cfg( not( f128_enabled) ) ]
137
+ unreachable ! ( ) ;
138
+ #[ cfg( f128_enabled) ]
139
+ ( f128:: EXP_MAX , f128:: EXP_MIN , f128:: EXP_MIN_SUBNORM )
140
+ }
141
+ } ;
142
+
143
+ // `scalbn`/`ldexp` have their trickiest behavior around exponent limits
144
+ int_count_around ( emax. cast ( ) , near_points, & mut values) ;
145
+ int_count_around ( emin. cast ( ) , near_points, & mut values) ;
146
+ int_count_around ( emin_sn. cast ( ) , near_points, & mut values) ;
147
+ int_count_around ( ( -emin_sn) . cast ( ) , near_points, & mut values) ;
148
+
149
+ // Also check values that cause the maximum possible difference in exponents
150
+ int_count_around ( ( emax - emin) . cast ( ) , near_points, & mut values) ;
151
+ int_count_around ( ( emin - emax) . cast ( ) , near_points, & mut values) ;
152
+ int_count_around ( ( emax - emin_sn) . cast ( ) , near_points, & mut values) ;
153
+ int_count_around ( ( emin_sn - emax) . cast ( ) , near_points, & mut values) ;
129
154
}
130
155
131
156
values. sort ( ) ;
132
157
values. dedup ( ) ;
133
- let len = values. len ( ) . try_into ( ) . unwrap ( ) ;
134
- ( values. into_iter ( ) , len)
158
+ let count = values. len ( ) . try_into ( ) . unwrap ( ) ;
159
+
160
+ test_log ( & format ! (
161
+ "{gen_kind:?} {basis:?} {fn_ident} arg {arg}/{args}: {count} edge cases" ,
162
+ gen_kind = ctx. gen_kind,
163
+ basis = ctx. basis,
164
+ fn_ident = ctx. fn_ident,
165
+ arg = argnum + 1 ,
166
+ args = ctx. input_count( ) ,
167
+ ) ) ;
168
+
169
+ ( values. into_iter ( ) , count)
170
+ }
171
+
172
+ /// Add `points` values both up and down, starting at and including `x`.
173
+ fn int_count_around < I : Int > ( x : I , points : u64 , values : & mut Vec < I > ) {
174
+ let mut current = x;
175
+ for _ in 0 ..points {
176
+ values. push ( current) ;
177
+ current = match current. checked_add ( I :: ONE ) {
178
+ Some ( v) => v,
179
+ None => break ,
180
+ } ;
181
+ }
182
+
183
+ current = x;
184
+ for _ in 0 ..points {
185
+ values. push ( current) ;
186
+ current = match current. checked_sub ( I :: ONE ) {
187
+ Some ( v) => v,
188
+ None => break ,
189
+ } ;
190
+ }
135
191
}
136
192
137
193
macro_rules! impl_edge_case_input {
0 commit comments