@@ -455,13 +455,8 @@ pub(crate) fn max(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
455
455
Ok ( Number :: Fixnum ( n2) )
456
456
}
457
457
}
458
- ( Number :: Integer ( n1) , Number :: Integer ( n2) ) => {
459
- if n1 > n2 {
460
- Ok ( Number :: Integer ( n1) )
461
- } else {
462
- Ok ( Number :: Integer ( n2) )
463
- }
464
- }
458
+ ( Number :: Integer ( n1) , Number :: Integer ( n2) ) => Ok ( Number :: Integer ( cmp:: max ( n1, n2) ) ) ,
459
+ ( Number :: Rational ( r1) , Number :: Rational ( r2) ) => Ok ( Number :: Rational ( cmp:: max ( r1, r2) ) ) ,
465
460
( n1, n2) => {
466
461
let stub_gen = || {
467
462
let max_atom = atom ! ( "max" ) ;
@@ -471,7 +466,15 @@ pub(crate) fn max(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
471
466
let f1 = try_numeric_result ! ( result_f( & n1) , stub_gen) ?;
472
467
let f2 = try_numeric_result ! ( result_f( & n2) , stub_gen) ?;
473
468
474
- Ok ( Number :: Float ( cmp:: max ( OrderedFloat ( f1) , OrderedFloat ( f2) ) ) )
469
+ match OrderedFloat ( f1) . cmp ( & OrderedFloat ( f2) ) {
470
+ cmp:: Ordering :: Less => Ok ( n2) ,
471
+ cmp:: Ordering :: Equal => {
472
+ // Note: n1 and n2 were compared as floats,
473
+ // so we return the second argument as a floating point value.
474
+ Ok ( Number :: Float ( OrderedFloat ( f2) ) )
475
+ }
476
+ cmp:: Ordering :: Greater => Ok ( n1) ,
477
+ }
475
478
}
476
479
}
477
480
}
@@ -499,13 +502,8 @@ pub(crate) fn min(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
499
502
Ok ( Number :: Fixnum ( n2) )
500
503
}
501
504
}
502
- ( Number :: Integer ( n1) , Number :: Integer ( n2) ) => {
503
- if n1 < n2 {
504
- Ok ( Number :: Integer ( n1) )
505
- } else {
506
- Ok ( Number :: Integer ( n2) )
507
- }
508
- }
505
+ ( Number :: Integer ( n1) , Number :: Integer ( n2) ) => Ok ( Number :: Integer ( cmp:: min ( n1, n2) ) ) ,
506
+ ( Number :: Rational ( r1) , Number :: Rational ( r2) ) => Ok ( Number :: Rational ( cmp:: min ( r1, r2) ) ) ,
509
507
( n1, n2) => {
510
508
let stub_gen = || {
511
509
let min_atom = atom ! ( "min" ) ;
@@ -515,7 +513,15 @@ pub(crate) fn min(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
515
513
let f1 = try_numeric_result ! ( result_f( & n1) , stub_gen) ?;
516
514
let f2 = try_numeric_result ! ( result_f( & n2) , stub_gen) ?;
517
515
518
- Ok ( Number :: Float ( cmp:: min ( OrderedFloat ( f1) , OrderedFloat ( f2) ) ) )
516
+ match OrderedFloat ( f1) . cmp ( & OrderedFloat ( f2) ) {
517
+ cmp:: Ordering :: Less => Ok ( n1) ,
518
+ cmp:: Ordering :: Equal => {
519
+ // Note: n1 and n2 were compared as floats,
520
+ // so we return the first argument as a floating point value.
521
+ Ok ( Number :: Float ( OrderedFloat ( f1) ) )
522
+ }
523
+ cmp:: Ordering :: Greater => Ok ( n2) ,
524
+ }
519
525
}
520
526
}
521
527
}
@@ -623,103 +629,110 @@ pub(crate) fn int_floor_div(
623
629
idiv ( n1, n2, arena)
624
630
}
625
631
626
- pub ( crate ) fn shr ( n1 : Number , n2 : Number , arena : & mut Arena ) -> Result < Number , MachineStubGen > {
632
+ pub ( crate ) fn shr ( lhs : Number , rhs : Number , arena : & mut Arena ) -> Result < Number , MachineStubGen > {
627
633
let stub_gen = || {
628
634
let shr_atom = atom ! ( ">>" ) ;
629
635
functor_stub ( shr_atom, 2 )
630
636
} ;
631
637
632
- if n2 . is_integer ( ) && n2 . is_negative ( ) {
633
- return shl ( n1 , neg ( n2 , arena) , arena) ;
638
+ if rhs . is_integer ( ) && rhs . is_negative ( ) {
639
+ return shl ( lhs , neg ( rhs , arena) , arena) ;
634
640
}
635
641
636
- match ( n1, n2) {
637
- ( Number :: Fixnum ( n1) , Number :: Fixnum ( n2) ) => {
638
- let n1_i = n1. get_num ( ) ;
639
- let n2_i = n2. get_num ( ) ;
640
-
641
- // FIXME(arithmetic_overflow)
642
- // what should this do for too large n2,
643
- // - logical right shift should probably turn to 0
644
- // - arithmetic right shift should maybe differ for negative numbers
645
- //
646
- // note: negaitve n2 is already handled above
647
- #[ allow( arithmetic_overflow) ]
648
- if let Ok ( n2) = usize:: try_from ( n2_i) {
649
- Ok ( Number :: arena_from ( n1_i >> n2, arena) )
650
- } else {
651
- Ok ( Number :: arena_from ( n1_i >> usize:: MAX , arena) )
652
- }
653
- }
654
- ( Number :: Fixnum ( n1) , Number :: Integer ( n2) ) => {
655
- let n1 = Integer :: from ( n1. get_num ( ) ) ;
656
-
657
- let result: Result < usize , _ > = ( & * n2) . try_into ( ) ;
642
+ match lhs {
643
+ Number :: Fixnum ( lhs) => {
644
+ let rhs = match rhs {
645
+ Number :: Fixnum ( fix) => fix. get_num ( ) . try_into ( ) . unwrap_or ( u32:: MAX ) ,
646
+ Number :: Integer ( int) => ( & * int) . try_into ( ) . unwrap_or ( u32:: MAX ) ,
647
+ other => {
648
+ return Err ( numerical_type_error ( ValidType :: Integer , other, stub_gen) ) ;
649
+ }
650
+ } ;
658
651
659
- match result {
660
- Ok ( n2) => Ok ( Number :: arena_from ( n1 >> n2, arena) ) ,
661
- Err ( _) => Ok ( Number :: arena_from ( n1 >> usize:: MAX , arena) ) ,
662
- }
663
- }
664
- ( Number :: Integer ( n1) , Number :: Fixnum ( n2) ) => match usize:: try_from ( n2. get_num ( ) ) {
665
- Ok ( n2) => Ok ( Number :: arena_from ( Integer :: from ( & * n1 >> n2) , arena) ) ,
666
- _ => Ok ( Number :: arena_from ( Integer :: from ( & * n1 >> usize:: MAX ) , arena) ) ,
667
- } ,
668
- ( Number :: Integer ( n1) , Number :: Integer ( n2) ) => {
669
- let result: Result < usize , _ > = ( & * n2) . try_into ( ) ;
652
+ let res = lhs. get_num ( ) . checked_shr ( rhs) . unwrap_or ( 0 ) ;
653
+ Ok ( Number :: arena_from ( res, arena) )
654
+ }
655
+ Number :: Integer ( lhs) => {
656
+ // Note: bigints require `log(n)` bits of space. If `rhs > usize::MAX`,
657
+ // then this clamping only becomes an issue when `lhs < 2 ^ (usize::MAX)`:
658
+ // - on 32-bit systems, `lhs` would need to be 512MiB big (1/8th of the addressable memory)
659
+ // - on 64-bit systems, `lhs` would need to be 2EiB big (!!!)
660
+ let rhs = match rhs {
661
+ Number :: Fixnum ( fix) => fix. get_num ( ) . try_into ( ) . unwrap_or ( usize:: MAX ) ,
662
+ Number :: Integer ( int) => ( & * int) . try_into ( ) . unwrap_or ( usize:: MAX ) ,
663
+ other => {
664
+ return Err ( numerical_type_error ( ValidType :: Integer , other, stub_gen) ) ;
665
+ }
666
+ } ;
670
667
671
- match result {
672
- Ok ( n2) => Ok ( Number :: arena_from ( Integer :: from ( & * n1 >> n2) , arena) ) ,
673
- Err ( _) => Ok ( Number :: arena_from ( Integer :: from ( & * n1 >> usize:: MAX ) , arena) ) ,
674
- }
668
+ Ok ( Number :: arena_from ( Integer :: from ( & * lhs >> rhs) , arena) )
675
669
}
676
- ( Number :: Integer ( _) , n2) => Err ( numerical_type_error ( ValidType :: Integer , n2, stub_gen) ) ,
677
- ( Number :: Fixnum ( _) , n2) => Err ( numerical_type_error ( ValidType :: Integer , n2, stub_gen) ) ,
678
- ( n1, _) => Err ( numerical_type_error ( ValidType :: Integer , n1, stub_gen) ) ,
670
+ other => Err ( numerical_type_error ( ValidType :: Integer , other, stub_gen) ) ,
679
671
}
680
672
}
681
673
682
- pub ( crate ) fn shl ( n1 : Number , n2 : Number , arena : & mut Arena ) -> Result < Number , MachineStubGen > {
674
+ pub ( crate ) fn shl ( lhs : Number , rhs : Number , arena : & mut Arena ) -> Result < Number , MachineStubGen > {
683
675
let stub_gen = || {
684
676
let shl_atom = atom ! ( "<<" ) ;
685
677
functor_stub ( shl_atom, 2 )
686
678
} ;
687
679
688
- if n2 . is_integer ( ) && n2 . is_negative ( ) {
689
- return shr ( n1 , neg ( n2 , arena) , arena) ;
680
+ if rhs . is_integer ( ) && rhs . is_negative ( ) {
681
+ return shr ( lhs , neg ( rhs , arena) , arena) ;
690
682
}
691
683
692
- match ( n1, n2) {
693
- ( Number :: Fixnum ( n1) , Number :: Fixnum ( n2) ) => {
694
- let n1_i = n1. get_num ( ) ;
695
- let n2_i = n2. get_num ( ) ;
684
+ let rhs = match rhs {
685
+ Number :: Fixnum ( fix) => fix. get_num ( ) . try_into ( ) . unwrap_or ( usize:: MAX ) ,
686
+ Number :: Integer ( int) => ( & * int) . try_into ( ) . unwrap_or ( usize:: MAX ) ,
687
+ other => {
688
+ return Err ( numerical_type_error ( ValidType :: Integer , other, stub_gen) ) ;
689
+ }
690
+ } ;
691
+
692
+ match lhs {
693
+ Number :: Fixnum ( lhs) => {
694
+ let lhs = lhs. get_num ( ) ;
696
695
697
- if let Ok ( n2 ) = usize :: try_from ( n2_i ) {
698
- Ok ( Number :: arena_from ( n1_i << n2 , arena) )
696
+ if let Some ( res ) = checked_signed_shl ( lhs , rhs ) {
697
+ Ok ( Number :: arena_from ( res , arena) )
699
698
} else {
700
- let n1 = Integer :: from ( n1_i) ;
701
- Ok ( Number :: arena_from ( n1 << usize:: MAX , arena) )
699
+ let lhs = Integer :: from ( lhs) ;
700
+ Ok ( Number :: arena_from (
701
+ Integer :: from ( lhs << ( rhs as usize ) ) ,
702
+ arena,
703
+ ) )
702
704
}
703
705
}
704
- ( Number :: Fixnum ( n1) , Number :: Integer ( n2) ) => {
705
- let n1 = Integer :: from ( n1. get_num ( ) ) ;
706
+ Number :: Integer ( lhs) => Ok ( Number :: arena_from (
707
+ Integer :: from ( & * lhs << ( rhs as usize ) ) ,
708
+ arena,
709
+ ) ) ,
710
+ other => Err ( numerical_type_error ( ValidType :: Integer , other, stub_gen) ) ,
711
+ }
712
+ }
706
713
707
- match ( & * n2) . try_into ( ) as Result < usize , _ > {
708
- Ok ( n2) => Ok ( Number :: arena_from ( n1 << n2, arena) ) ,
709
- _ => Ok ( Number :: arena_from ( n1 << usize:: MAX , arena) ) ,
710
- }
714
+ /// Returns `x << shift`, checking for overflow and for values of `shift` that are too big.
715
+ #[ inline]
716
+ fn checked_signed_shl ( x : i64 , shift : usize ) -> Option < i64 > {
717
+ if shift == 0 {
718
+ return Some ( x) ;
719
+ }
720
+
721
+ if x >= 0 {
722
+ // Note: for unsigned integers, the condition would usually be spelled
723
+ // `shift <= x.leading_zeros()`, but since the MSB for signed integers
724
+ // controls the sign, we need to make sure that `shift` is at most
725
+ // `x.leading_zeros() - 1`.
726
+ if shift < x. leading_zeros ( ) as usize {
727
+ Some ( x << shift)
728
+ } else {
729
+ None
711
730
}
712
- ( Number :: Integer ( n1) , Number :: Fixnum ( n2) ) => match usize:: try_from ( n2. get_num ( ) ) {
713
- Ok ( n2) => Ok ( Number :: arena_from ( Integer :: from ( & * n1 << n2) , arena) ) ,
714
- _ => Ok ( Number :: arena_from ( Integer :: from ( & * n1 << usize:: MAX ) , arena) ) ,
715
- } ,
716
- ( Number :: Integer ( n1) , Number :: Integer ( n2) ) => match ( & * n2) . try_into ( ) as Result < usize , _ > {
717
- Ok ( n2) => Ok ( Number :: arena_from ( Integer :: from ( & * n1 << n2) , arena) ) ,
718
- _ => Ok ( Number :: arena_from ( Integer :: from ( & * n1 << usize:: MAX ) , arena) ) ,
719
- } ,
720
- ( Number :: Integer ( _) , n2) => Err ( numerical_type_error ( ValidType :: Integer , n2, stub_gen) ) ,
721
- ( Number :: Fixnum ( _) , n2) => Err ( numerical_type_error ( ValidType :: Integer , n2, stub_gen) ) ,
722
- ( n1, _) => Err ( numerical_type_error ( ValidType :: Integer , n1, stub_gen) ) ,
731
+ } else {
732
+ let y = x. checked_neg ( ) ?;
733
+ // FIXME: incorrectly rejects `-2 ^ 62 << 1`. This is currently a non-issue,
734
+ // since the bitshift is then done as a `Number::Integer`
735
+ checked_signed_shl ( y, shift) . and_then ( |res| res. checked_neg ( ) )
723
736
}
724
737
}
725
738
@@ -947,8 +960,7 @@ pub(crate) fn gcd(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
947
960
Ok ( Number :: arena_from ( Integer :: from ( n2_clone. gcd ( & n1) ) , arena) )
948
961
}
949
962
( Number :: Integer ( n1) , Number :: Integer ( n2) ) => {
950
- let n2: isize = ( & * n2) . try_into ( ) . unwrap ( ) ;
951
- let value: Integer = ( & * n1) . gcd ( & Integer :: from ( n2) ) . into ( ) ;
963
+ let value: Integer = ( & * n1) . gcd ( & * n2) . into ( ) ;
952
964
Ok ( Number :: arena_from ( value, arena) )
953
965
}
954
966
( Number :: Float ( f) , _) | ( _, Number :: Float ( f) ) => {
@@ -1044,7 +1056,12 @@ pub(crate) fn sqrt(n1: Number) -> Result<f64, MachineStubGen> {
1044
1056
1045
1057
#[ inline]
1046
1058
pub ( crate ) fn floor ( n1 : Number , arena : & mut Arena ) -> Number {
1047
- rnd_i ( & n1, arena)
1059
+ rnd_i ( & n1, arena) . unwrap_or_else ( |_err| {
1060
+ // FIXME: Currently floor/1 (and several call sites) are infallible,
1061
+ // but the failing cases (when `n1` is `Number::Float(NAN)` or `Number::Float(INFINITY)`)
1062
+ // are not reachable with standard is/2 operations.
1063
+ todo ! ( "Make floor/1 fallible" ) ;
1064
+ } )
1048
1065
}
1049
1066
1050
1067
#[ inline]
@@ -1067,16 +1084,22 @@ pub(crate) fn truncate(n: Number, arena: &mut Arena) -> Number {
1067
1084
}
1068
1085
}
1069
1086
1070
- pub ( crate ) fn round ( n : Number , arena : & mut Arena ) -> Result < Number , MachineStubGen > {
1071
- let stub_gen = || {
1072
- let is_atom = atom ! ( "is" ) ;
1073
- functor_stub ( is_atom, 2 )
1087
+ pub ( crate ) fn round ( num : Number , arena : & mut Arena ) -> Result < Number , MachineStubGen > {
1088
+ let res = match num {
1089
+ Number :: Fixnum ( _) | Number :: Integer ( _) => num,
1090
+ Number :: Rational ( rat) => Number :: arena_from ( rat. round ( ) , arena) ,
1091
+ Number :: Float ( f) => Number :: Float ( OrderedFloat ( ( * f) . round ( ) ) ) ,
1074
1092
} ;
1075
1093
1076
- let result = add ( n, Number :: Float ( OrderedFloat ( 0.5f64 ) ) , arena) ;
1077
- let result = try_numeric_result ! ( result, stub_gen) ?;
1094
+ // FIXME: make round/1 return EvalError
1095
+ rnd_i ( & res, arena) . map_err ( |err| -> MachineStubGen {
1096
+ Box :: new ( move |machine_st| {
1097
+ let eval_error = machine_st. evaluation_error ( err) ;
1098
+ let stub = functor_stub ( atom ! ( "round" ) , 1 ) ;
1078
1099
1079
- Ok ( floor ( result, arena) )
1100
+ machine_st. error_form ( eval_error, stub)
1101
+ } )
1102
+ } )
1080
1103
}
1081
1104
1082
1105
pub ( crate ) fn bitwise_complement ( n1 : Number , arena : & mut Arena ) -> Result < Number , MachineStubGen > {
0 commit comments