@@ -267,6 +267,60 @@ impl FPU {
267267 {
268268 self . flagged ( a. borrow ( ) . sqrt ( rnd, self . detect_tininess ) )
269269 }
270+
271+ #[ inline]
272+ #[ must_use]
273+ pub fn max < F , T > ( & mut self , a : T , b : T ) -> F
274+ where
275+ F : Float ,
276+ T : Borrow < F > ,
277+ {
278+ let fs1_val = a. borrow ( ) ;
279+ let fs2_val = b. borrow ( ) ;
280+ let fs2_nan = fs2_val. is_nan ( ) ;
281+ let fs1_nan = fs1_val. is_nan ( ) ;
282+ if fs1_nan && fs2_nan {
283+ F :: from_bits ( F :: DEFAULT_NAN )
284+ }
285+ else {
286+ let is_lt = self . lt_quiet :: < F , & F > ( fs2_val, fs1_val) ;
287+ let is_eq = self . eq :: < F , & F > ( fs2_val, fs1_val) ;
288+ let greater = is_lt || ( is_eq && ( fs1_val. is_positive ( ) || !fs2_val. is_positive ( ) ) ) ;
289+ if greater || fs2_nan {
290+ F :: from_bits ( fs1_val. to_bits ( ) )
291+ }
292+ else {
293+ F :: from_bits ( fs2_val. to_bits ( ) )
294+ }
295+ }
296+ }
297+
298+ #[ inline]
299+ #[ must_use]
300+ pub fn min < F , T > ( & mut self , a : T , b : T ) -> F
301+ where
302+ F : Float ,
303+ T : Borrow < F > ,
304+ {
305+ let fs1_val = a. borrow ( ) ;
306+ let fs2_val = b. borrow ( ) ;
307+ let fs2_nan = fs2_val. is_nan ( ) ;
308+ let fs1_nan = fs1_val. is_nan ( ) ;
309+ if fs1_nan && fs2_nan {
310+ F :: from_bits ( F :: DEFAULT_NAN )
311+ }
312+ else {
313+ let is_lt = self . lt_quiet :: < F , & F > ( fs1_val, fs2_val) ;
314+ let is_eq = self . eq :: < F , & F > ( fs1_val, fs2_val) ;
315+ let less = is_lt || ( is_eq && !fs1_val. is_positive ( ) ) ;
316+ if less || fs2_nan {
317+ F :: from_bits ( fs1_val. to_bits ( ) )
318+ }
319+ else {
320+ F :: from_bits ( fs2_val. to_bits ( ) )
321+ }
322+ }
323+ }
270324}
271325
272326impl FPU {
@@ -320,3 +374,176 @@ impl FPU {
320374 ui32_to_f64 ( a)
321375 }
322376}
377+
378+ #[ cfg( test) ]
379+ mod tests {
380+ use super :: * ;
381+
382+ // Helper functions to create specific float values
383+ fn make_f32 ( v : f32 ) -> float32_t {
384+ float32_t:: from_bits ( v. to_bits ( ) )
385+ }
386+
387+ fn make_f64 ( v : f64 ) -> float64_t {
388+ float64_t:: from_bits ( v. to_bits ( ) )
389+ }
390+
391+ fn get_f32 ( v : float32_t ) -> f32 {
392+ f32:: from_bits ( v. to_bits ( ) )
393+ }
394+
395+ fn get_f64 ( v : float64_t ) -> f64 {
396+ f64:: from_bits ( v. to_bits ( ) )
397+ }
398+
399+ #[ test]
400+ fn test_max_f32_normal_values ( ) {
401+ let mut fpu = FPU :: default ( ) ;
402+
403+ // Test regular positive values
404+ assert_eq ! ( get_f32( fpu. max( make_f32( 1.0 ) , make_f32( 2.0 ) ) ) , 2.0 ) ;
405+ assert_eq ! ( get_f32( fpu. max( make_f32( 2.0 ) , make_f32( 1.0 ) ) ) , 2.0 ) ;
406+
407+ // Test regular negative values
408+ assert_eq ! ( get_f32( fpu. max( make_f32( -1.0 ) , make_f32( -2.0 ) ) ) , -1.0 ) ;
409+ assert_eq ! ( get_f32( fpu. max( make_f32( -2.0 ) , make_f32( -1.0 ) ) ) , -1.0 ) ;
410+
411+ // Test mixed sign values
412+ assert_eq ! ( get_f32( fpu. max( make_f32( -1.0 ) , make_f32( 1.0 ) ) ) , 1.0 ) ;
413+ assert_eq ! ( get_f32( fpu. max( make_f32( 1.0 ) , make_f32( -1.0 ) ) ) , 1.0 ) ;
414+ }
415+
416+ #[ test]
417+ fn test_max_f32_special_values ( ) {
418+ let mut fpu = FPU :: default ( ) ;
419+
420+ // Test with infinities
421+ assert_eq ! ( get_f32( fpu. max( make_f32( f32 :: INFINITY ) , make_f32( 1.0 ) ) ) , f32 :: INFINITY ) ;
422+ assert_eq ! ( get_f32( fpu. max( make_f32( 1.0 ) , make_f32( f32 :: INFINITY ) ) ) , f32 :: INFINITY ) ;
423+ assert_eq ! ( get_f32( fpu. max( make_f32( f32 :: NEG_INFINITY ) , make_f32( 1.0 ) ) ) , 1.0 ) ;
424+ assert_eq ! ( get_f32( fpu. max( make_f32( 1.0 ) , make_f32( f32 :: NEG_INFINITY ) ) ) , 1.0 ) ;
425+ assert_eq ! ( get_f32( fpu. max( make_f32( f32 :: INFINITY ) , make_f32( f32 :: NEG_INFINITY ) ) ) , f32 :: INFINITY ) ;
426+
427+ // Test with NaN
428+ let nan = f32:: NAN ;
429+ let result1 = get_f32 ( fpu. max ( make_f32 ( nan) , make_f32 ( 1.0 ) ) ) ;
430+ let result2 = get_f32 ( fpu. max ( make_f32 ( 1.0 ) , make_f32 ( nan) ) ) ;
431+
432+ assert_eq ! ( result1, 1.0 ) ; // NaN + non-NaN should return non-NaN
433+ assert_eq ! ( result2, 1.0 ) ; // non-NaN + NaN should return non-NaN
434+
435+ // Both NaN should return a canonical NaN
436+ let result_both_nan = fpu. max ( make_f32 ( nan) , make_f32 ( nan) ) ;
437+ assert ! ( get_f32( result_both_nan) . is_nan( ) ) ;
438+
439+ // Test with zero
440+ let plus_zero = 0.0f32 ;
441+ let minus_zero = -0.0f32 ;
442+
443+ // Max should prefer +0 over -0
444+ let zero_result = get_f32 ( fpu. max ( make_f32 ( plus_zero) , make_f32 ( minus_zero) ) ) ;
445+ assert ! ( zero_result == 0.0 && zero_result. is_sign_positive( ) ) ;
446+
447+ let zero_result = get_f32 ( fpu. max ( make_f32 ( minus_zero) , make_f32 ( plus_zero) ) ) ;
448+ assert ! ( zero_result == 0.0 && zero_result. is_sign_positive( ) ) ;
449+ }
450+
451+ #[ test]
452+ fn test_min_f32_normal_values ( ) {
453+ let mut fpu = FPU :: default ( ) ;
454+
455+ // Test regular positive values
456+ assert_eq ! ( get_f32( fpu. min( make_f32( 1.0 ) , make_f32( 2.0 ) ) ) , 1.0 ) ;
457+ assert_eq ! ( get_f32( fpu. min( make_f32( 2.0 ) , make_f32( 1.0 ) ) ) , 1.0 ) ;
458+
459+ // Test regular negative values
460+ assert_eq ! ( get_f32( fpu. min( make_f32( -1.0 ) , make_f32( -2.0 ) ) ) , -2.0 ) ;
461+ assert_eq ! ( get_f32( fpu. min( make_f32( -2.0 ) , make_f32( -1.0 ) ) ) , -2.0 ) ;
462+
463+ // Test mixed sign values
464+ assert_eq ! ( get_f32( fpu. min( make_f32( -1.0 ) , make_f32( 1.0 ) ) ) , -1.0 ) ;
465+ assert_eq ! ( get_f32( fpu. min( make_f32( 1.0 ) , make_f32( -1.0 ) ) ) , -1.0 ) ;
466+ }
467+
468+ #[ test]
469+ fn test_min_f32_special_values ( ) {
470+ let mut fpu = FPU :: default ( ) ;
471+
472+ // Test with infinities
473+ assert_eq ! ( get_f32( fpu. min( make_f32( f32 :: INFINITY ) , make_f32( 1.0 ) ) ) , 1.0 ) ;
474+ assert_eq ! ( get_f32( fpu. min( make_f32( 1.0 ) , make_f32( f32 :: INFINITY ) ) ) , 1.0 ) ;
475+ assert_eq ! ( get_f32( fpu. min( make_f32( f32 :: NEG_INFINITY ) , make_f32( 1.0 ) ) ) , f32 :: NEG_INFINITY ) ;
476+ assert_eq ! ( get_f32( fpu. min( make_f32( 1.0 ) , make_f32( f32 :: NEG_INFINITY ) ) ) , f32 :: NEG_INFINITY ) ;
477+ assert_eq ! ( get_f32( fpu. min( make_f32( f32 :: INFINITY ) , make_f32( f32 :: NEG_INFINITY ) ) ) , f32 :: NEG_INFINITY ) ;
478+
479+ // Test with NaN
480+ let nan = f32:: NAN ;
481+ let result1 = get_f32 ( fpu. min ( make_f32 ( nan) , make_f32 ( 1.0 ) ) ) ;
482+ let result2 = get_f32 ( fpu. min ( make_f32 ( 1.0 ) , make_f32 ( nan) ) ) ;
483+
484+ assert_eq ! ( result1, 1.0 ) ; // NaN + non-NaN should return non-NaN
485+ assert_eq ! ( result2, 1.0 ) ; // non-NaN + NaN should return non-NaN
486+
487+ // Both NaN should return a canonical NaN
488+ let result_both_nan = fpu. min ( make_f32 ( nan) , make_f32 ( nan) ) ;
489+ assert ! ( get_f32( result_both_nan) . is_nan( ) ) ;
490+
491+ // Test with zero
492+ let plus_zero = 0.0f32 ;
493+ let minus_zero = -0.0f32 ;
494+
495+ // Min should prefer -0 over +0
496+ let zero_result = get_f32 ( fpu. min ( make_f32 ( plus_zero) , make_f32 ( minus_zero) ) ) ;
497+ assert ! ( zero_result == 0.0 && zero_result. is_sign_negative( ) ) ;
498+
499+ let zero_result = get_f32 ( fpu. min ( make_f32 ( minus_zero) , make_f32 ( plus_zero) ) ) ;
500+ assert ! ( zero_result == 0.0 && zero_result. is_sign_negative( ) ) ;
501+
502+ let zero_result = get_f32 ( fpu. min ( make_f32 ( plus_zero) , make_f32 ( minus_zero) ) ) ;
503+ assert ! ( zero_result == 0.0 && zero_result. is_sign_negative( ) ) ;
504+ }
505+
506+ #[ test]
507+ fn test_max_f64_normal_values ( ) {
508+ let mut fpu = FPU :: default ( ) ;
509+
510+ // Test regular positive values
511+ assert_eq ! ( get_f64( fpu. max( make_f64( 1.0 ) , make_f64( 2.0 ) ) ) , 2.0 ) ;
512+ assert_eq ! ( get_f64( fpu. max( make_f64( 2.0 ) , make_f64( 1.0 ) ) ) , 2.0 ) ;
513+
514+ // Test regular negative values
515+ assert_eq ! ( get_f64( fpu. max( make_f64( -1.0 ) , make_f64( -2.0 ) ) ) , -1.0 ) ;
516+ assert_eq ! ( get_f64( fpu. max( make_f64( -2.0 ) , make_f64( -1.0 ) ) ) , -1.0 ) ;
517+
518+ // Test mixed sign values
519+ assert_eq ! ( get_f64( fpu. max( make_f64( -1.0 ) , make_f64( 1.0 ) ) ) , 1.0 ) ;
520+ assert_eq ! ( get_f64( fpu. max( make_f64( 1.0 ) , make_f64( -1.0 ) ) ) , 1.0 ) ;
521+ }
522+
523+ #[ test]
524+ fn test_min_f64_special_values ( ) {
525+ let mut fpu = FPU :: default ( ) ;
526+
527+ // Test with infinities
528+ assert_eq ! ( get_f64( fpu. min( make_f64( f64 :: INFINITY ) , make_f64( 1.0 ) ) ) , 1.0 ) ;
529+ assert_eq ! ( get_f64( fpu. min( make_f64( 1.0 ) , make_f64( f64 :: INFINITY ) ) ) , 1.0 ) ;
530+ assert_eq ! ( get_f64( fpu. min( make_f64( f64 :: NEG_INFINITY ) , make_f64( 1.0 ) ) ) , f64 :: NEG_INFINITY ) ;
531+ assert_eq ! ( get_f64( fpu. min( make_f64( 1.0 ) , make_f64( f64 :: NEG_INFINITY ) ) ) , f64 :: NEG_INFINITY ) ;
532+
533+ // Test with NaN
534+ let nan = f64:: NAN ;
535+ let result1 = get_f64 ( fpu. min ( make_f64 ( nan) , make_f64 ( 1.0 ) ) ) ;
536+ let result2 = get_f64 ( fpu. min ( make_f64 ( 1.0 ) , make_f64 ( nan) ) ) ;
537+
538+ assert_eq ! ( result1, 1.0 ) ; // NaN + non-NaN should return non-NaN
539+ assert_eq ! ( result2, 1.0 ) ; // non-NaN + NaN should return non-NaN
540+
541+ // Test with zero
542+ let plus_zero = 0.0f64 ;
543+ let minus_zero = -0.0f64 ;
544+
545+ // Min should prefer -0 over +0
546+ let zero_result = get_f64 ( fpu. min ( make_f64 ( plus_zero) , make_f64 ( minus_zero) ) ) ;
547+ assert ! ( zero_result == 0.0 && zero_result. is_sign_negative( ) ) ;
548+ }
549+ }
0 commit comments