11//! A generator that checks a handful of cases near infinities, zeros, asymptotes, and NaNs.
22
3- use libm:: support:: { Float , Int } ;
3+ use libm:: support:: { CastInto , Float , Int } ;
44
55use crate :: domain:: get_domain;
66use crate :: gen:: KnownSize ;
77use 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} ;
99
1010/// Generate a sequence of edge cases, e.g. numbers near zeroes and infiniteis.
1111pub trait EdgeCaseInput < Op > {
7878 ( ret. into_iter ( ) , count)
7979}
8080
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
8282/// increments (1 ULP).
8383fn count_up < F : Float > ( mut x : F , points : u64 , values : & mut Vec < F > ) {
8484 assert ! ( !x. is_nan( ) ) ;
@@ -91,7 +91,7 @@ fn count_up<F: Float>(mut x: F, points: u64, values: &mut Vec<F>) {
9191 }
9292}
9393
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
9595/// increments (1 ULP).
9696fn count_down < F : Float > ( mut x : F , points : u64 , values : & mut Vec < F > ) {
9797 assert ! ( !x. is_nan( ) ) ;
@@ -107,31 +107,87 @@ fn count_down<F: Float>(mut x: F, points: u64, values: &mut Vec<F>) {
107107/// Create a list of values around interesting integer points (min, zero, max).
108108pub fn int_edge_cases < I : Int > (
109109 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+ {
112115 let mut values = Vec :: new ( ) ;
113116 let near_points = check_near_count ( ctx) ;
114117
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) ;
129154 }
130155
131156 values. sort ( ) ;
132157 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+ }
135191}
136192
137193macro_rules! impl_edge_case_input {
0 commit comments