@@ -10,14 +10,14 @@ use std::error::Error;
10
10
use std:: fmt:: Write as _0;
11
11
use std:: fs:: { self , File } ;
12
12
use std:: io:: Write as _1;
13
- use std:: { i16, u32, u8} ;
13
+ use std:: { i16, u16 , u32, u64 , u8} ;
14
14
15
15
use rand:: { Rng , SeedableRng , XorShiftRng } ;
16
16
17
17
// Number of test cases to generate
18
18
const NTESTS : usize = 10_000 ;
19
19
20
- // TODO tweak this function to generate edge cases (zero, infinity, NaN) more often
20
+ // TODO tweak these functions to generate edge cases (zero, infinity, NaN) more often
21
21
fn f32 ( rng : & mut XorShiftRng ) -> f32 {
22
22
let sign = if rng. gen_bool ( 0.5 ) { 1 << 31 } else { 0 } ;
23
23
let exponent = ( rng. gen_range ( 0 , u8:: MAX ) as u32 ) << 23 ;
@@ -26,13 +26,21 @@ fn f32(rng: &mut XorShiftRng) -> f32 {
26
26
f32:: from_bits ( sign + exponent + mantissa)
27
27
}
28
28
29
+ fn f64 ( rng : & mut XorShiftRng ) -> f64 {
30
+ let sign = if rng. gen_bool ( 0.5 ) { 1 << 63 } else { 0 } ;
31
+ let exponent = ( rng. gen_range ( 0 , u16:: MAX ) as u64 & ( ( 1 << 11 ) - 1 ) ) << 52 ;
32
+ let mantissa = rng. gen_range ( 0 , u64:: MAX ) & ( ( 1 << 52 ) - 1 ) ;
33
+
34
+ f64:: from_bits ( sign + exponent + mantissa)
35
+ }
36
+
29
37
// fn(f32) -> f32
30
38
macro_rules! f32_f32 {
31
- ( $( $intr: ident, ) + ) => {
39
+ ( $( $intr: ident, ) * ) => {
32
40
fn f32_f32( rng: & mut XorShiftRng ) -> Result <( ) , Box <Error >> {
33
41
// MUSL C implementation of the function to test
34
42
extern "C" {
35
- $( fn $intr( _: f32 ) -> f32 ; ) +
43
+ $( fn $intr( _: f32 ) -> f32 ; ) *
36
44
}
37
45
38
46
$(
@@ -78,18 +86,19 @@ macro_rules! f32_f32 {
78
86
" ,
79
87
stringify!( $intr) ,
80
88
cases) ?;
81
- ) +
89
+ ) *
82
90
83
91
Ok ( ( ) )
84
92
}
85
93
}
86
94
}
87
95
96
+ // fn(f32, f32) -> f32
88
97
macro_rules! f32f32_f32 {
89
- ( $( $intr: ident, ) + ) => {
98
+ ( $( $intr: ident, ) * ) => {
90
99
fn f32f32_f32( rng: & mut XorShiftRng ) -> Result <( ) , Box <Error >> {
91
100
extern "C" {
92
- $( fn $intr( _: f32 , _: f32 ) -> f32 ; ) +
101
+ $( fn $intr( _: f32 , _: f32 ) -> f32 ; ) *
93
102
}
94
103
95
104
$(
@@ -137,18 +146,19 @@ macro_rules! f32f32_f32 {
137
146
" ,
138
147
stringify!( $intr) ,
139
148
cases) ?;
140
- ) +
149
+ ) *
141
150
142
151
Ok ( ( ) )
143
152
}
144
153
} ;
145
154
}
146
155
156
+ // fn(f32, i32) -> f32
147
157
macro_rules! f32i32_f32 {
148
- ( $( $intr: ident, ) + ) => {
158
+ ( $( $intr: ident, ) * ) => {
149
159
fn f32i32_f32( rng: & mut XorShiftRng ) -> Result <( ) , Box <Error >> {
150
160
extern "C" {
151
- $( fn $intr( _: f32 , _: i32 ) -> f32 ; ) +
161
+ $( fn $intr( _: f32 , _: i32 ) -> f32 ; ) *
152
162
}
153
163
154
164
$(
@@ -195,7 +205,185 @@ macro_rules! f32i32_f32 {
195
205
" ,
196
206
stringify!( $intr) ,
197
207
cases) ?;
198
- ) +
208
+ ) *
209
+
210
+ Ok ( ( ) )
211
+ }
212
+ } ;
213
+ }
214
+
215
+ // fn(f64) -> f64
216
+ macro_rules! f64_f64 {
217
+ ( $( $intr: ident, ) * ) => {
218
+ fn f64_f64( rng: & mut XorShiftRng ) -> Result <( ) , Box <Error >> {
219
+ // MUSL C implementation of the function to test
220
+ extern "C" {
221
+ $( fn $intr( _: f64 ) -> f64 ; ) *
222
+ }
223
+
224
+ $(
225
+ let mut cases = String :: new( ) ;
226
+ for _ in 0 ..NTESTS {
227
+ let inp = f64 ( rng) ;
228
+ let out = unsafe { $intr( inp) } ;
229
+
230
+ let inp = inp. to_bits( ) ;
231
+ let out = out. to_bits( ) ;
232
+
233
+ write!( cases, "({}, {})" , inp, out) . unwrap( ) ;
234
+ cases. push( ',' ) ;
235
+ }
236
+
237
+ let mut f = File :: create( concat!( "tests/" , stringify!( $intr) , ".rs" ) ) ?;
238
+ write!( f, "
239
+ extern crate libm;
240
+
241
+ #[test]
242
+ fn {0}() {{
243
+ const CASES: &[(u64, u64)] = &[
244
+ {1}
245
+ ];
246
+
247
+ for case in CASES {{
248
+ let (inp, expected) = *case;
249
+
250
+ let outf = libm::{0}(f64::from_bits(inp));
251
+ let outi = outf.to_bits();
252
+
253
+ if !((outf.is_nan() && f64::from_bits(expected).is_nan()) ||
254
+ libm::_eq(outi, expected)) {{
255
+ panic!(
256
+ \" input: {{}}, output: {{}}, expected: {{}}\" ,
257
+ inp,
258
+ outi,
259
+ expected,
260
+ );
261
+ }}
262
+ }}
263
+ }}
264
+ " ,
265
+ stringify!( $intr) ,
266
+ cases) ?;
267
+ ) *
268
+
269
+ Ok ( ( ) )
270
+ }
271
+ }
272
+ }
273
+
274
+ // fn(f64, f64) -> f64
275
+ macro_rules! f64f64_f64 {
276
+ ( $( $intr: ident, ) * ) => {
277
+ fn f64f64_f64( rng: & mut XorShiftRng ) -> Result <( ) , Box <Error >> {
278
+ extern "C" {
279
+ $( fn $intr( _: f64 , _: f64 ) -> f64 ; ) *
280
+ }
281
+
282
+ $(
283
+ let mut cases = String :: new( ) ;
284
+ for _ in 0 ..NTESTS {
285
+ let i1 = f64 ( rng) ;
286
+ let i2 = f64 ( rng) ;
287
+ let out = unsafe { $intr( i1, i2) } ;
288
+
289
+ let i1 = i1. to_bits( ) ;
290
+ let i2 = i2. to_bits( ) ;
291
+ let out = out. to_bits( ) ;
292
+
293
+ write!( cases, "(({}, {}), {})" , i1, i2, out) . unwrap( ) ;
294
+ cases. push( ',' ) ;
295
+ }
296
+
297
+ let mut f = File :: create( concat!( "tests/" , stringify!( $intr) , ".rs" ) ) ?;
298
+ write!( f, "
299
+ extern crate libm;
300
+
301
+ #[test]
302
+ fn {0}() {{
303
+ const CASES: &[((u64, u64), u64)] = &[
304
+ {1}
305
+ ];
306
+
307
+ for case in CASES {{
308
+ let ((i1, i2), expected) = *case;
309
+
310
+ let outf = libm::{0}(f64::from_bits(i1), f64::from_bits(i2));
311
+ let outi = outf.to_bits();
312
+
313
+ if !((outf.is_nan() && f64::from_bits(expected).is_nan()) ||
314
+ libm::_eq(outi, expected)) {{
315
+ panic!(
316
+ \" input: {{:?}}, output: {{}}, expected: {{}}\" ,
317
+ (i1, i2),
318
+ outi,
319
+ expected,
320
+ );
321
+ }}
322
+ }}
323
+ }}
324
+ " ,
325
+ stringify!( $intr) ,
326
+ cases) ?;
327
+ ) *
328
+
329
+ Ok ( ( ) )
330
+ }
331
+ } ;
332
+ }
333
+
334
+ // fn(f64, i32) -> f64
335
+ macro_rules! f64i32_f64 {
336
+ ( $( $intr: ident, ) * ) => {
337
+ fn f64i32_f64( rng: & mut XorShiftRng ) -> Result <( ) , Box <Error >> {
338
+ extern "C" {
339
+ $( fn $intr( _: f64 , _: i32 ) -> f64 ; ) *
340
+ }
341
+
342
+ $(
343
+ let mut cases = String :: new( ) ;
344
+ for _ in 0 ..NTESTS {
345
+ let i1 = f64 ( rng) ;
346
+ let i2 = rng. gen_range( i16 :: MIN , i16 :: MAX ) ;
347
+ let out = unsafe { $intr( i1, i2 as i32 ) } ;
348
+
349
+ let i1 = i1. to_bits( ) ;
350
+ let out = out. to_bits( ) ;
351
+
352
+ write!( cases, "(({}, {}), {})" , i1, i2, out) . unwrap( ) ;
353
+ cases. push( ',' ) ;
354
+ }
355
+
356
+ let mut f = File :: create( concat!( "tests/" , stringify!( $intr) , ".rs" ) ) ?;
357
+ write!( f, "
358
+ extern crate libm;
359
+
360
+ #[test]
361
+ fn {0}() {{
362
+ const CASES: &[((u64, i16), u64)] = &[
363
+ {1}
364
+ ];
365
+
366
+ for case in CASES {{
367
+ let ((i1, i2), expected) = *case;
368
+
369
+ let outf = libm::{0}(f64::from_bits(i1), i2 as i32);
370
+ let outi = outf.to_bits();
371
+
372
+ if !((outf.is_nan() && f64::from_bits(expected).is_nan()) ||
373
+ libm::_eq(outi, expected)) {{
374
+ panic!(
375
+ \" input: {{:?}}, output: {{}}, expected: {{}}\" ,
376
+ (i1, i2),
377
+ outi,
378
+ expected,
379
+ );
380
+ }}
381
+ }}
382
+ }}
383
+ " ,
384
+ stringify!( $intr) ,
385
+ cases) ?;
386
+ ) *
199
387
200
388
Ok ( ( ) )
201
389
}
@@ -211,6 +399,9 @@ fn main() -> Result<(), Box<Error>> {
211
399
f32_f32 ( & mut rng) ?;
212
400
f32f32_f32 ( & mut rng) ?;
213
401
f32i32_f32 ( & mut rng) ?;
402
+ f64_f64 ( & mut rng) ?;
403
+ f64f64_f64 ( & mut rng) ?;
404
+ f64i32_f64 ( & mut rng) ?;
214
405
215
406
Ok ( ( ) )
216
407
}
@@ -233,3 +424,18 @@ f32f32_f32! {
233
424
f32i32_f32 ! {
234
425
scalbnf,
235
426
}
427
+
428
+ // With signature `fn(f64) -> f64`
429
+ f64_f64 ! {
430
+ fabs,
431
+ }
432
+
433
+ // With signature `fn(f64, f64) -> f64`
434
+ f64f64_f64 ! {
435
+ // fmod,
436
+ }
437
+
438
+ // With signature `fn(f64, i32) -> f64`
439
+ f64i32_f64 ! {
440
+ // scalbn,
441
+ }
0 commit comments