@@ -455,13 +455,8 @@ pub(crate) fn max(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
455455 Ok ( Number :: Fixnum ( n2) )
456456 }
457457 }
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) ) ) ,
465460 ( n1, n2) => {
466461 let stub_gen = || {
467462 let max_atom = atom ! ( "max" ) ;
@@ -471,7 +466,15 @@ pub(crate) fn max(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
471466 let f1 = try_numeric_result ! ( result_f( & n1) , stub_gen) ?;
472467 let f2 = try_numeric_result ! ( result_f( & n2) , stub_gen) ?;
473468
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+ }
475478 }
476479 }
477480}
@@ -499,13 +502,8 @@ pub(crate) fn min(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
499502 Ok ( Number :: Fixnum ( n2) )
500503 }
501504 }
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) ) ) ,
509507 ( n1, n2) => {
510508 let stub_gen = || {
511509 let min_atom = atom ! ( "min" ) ;
@@ -515,7 +513,15 @@ pub(crate) fn min(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
515513 let f1 = try_numeric_result ! ( result_f( & n1) , stub_gen) ?;
516514 let f2 = try_numeric_result ! ( result_f( & n2) , stub_gen) ?;
517515
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+ }
519525 }
520526 }
521527}
@@ -623,103 +629,110 @@ pub(crate) fn int_floor_div(
623629 idiv ( n1, n2, arena)
624630}
625631
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 > {
627633 let stub_gen = || {
628634 let shr_atom = atom ! ( ">>" ) ;
629635 functor_stub ( shr_atom, 2 )
630636 } ;
631637
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) ;
634640 }
635641
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+ } ;
658651
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+ } ;
670667
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) )
675669 }
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) ) ,
679671 }
680672}
681673
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 > {
683675 let stub_gen = || {
684676 let shl_atom = atom ! ( "<<" ) ;
685677 functor_stub ( shl_atom, 2 )
686678 } ;
687679
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) ;
690682 }
691683
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 ( ) ;
696695
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) )
699698 } 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+ ) )
702704 }
703705 }
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+ }
706713
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
711730 }
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 ( ) )
723736 }
724737}
725738
@@ -947,8 +960,7 @@ pub(crate) fn gcd(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
947960 Ok ( Number :: arena_from ( Integer :: from ( n2_clone. gcd ( & n1) ) , arena) )
948961 }
949962 ( 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 ( ) ;
952964 Ok ( Number :: arena_from ( value, arena) )
953965 }
954966 ( Number :: Float ( f) , _) | ( _, Number :: Float ( f) ) => {
@@ -1044,7 +1056,12 @@ pub(crate) fn sqrt(n1: Number) -> Result<f64, MachineStubGen> {
10441056
10451057#[ inline]
10461058pub ( 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+ } )
10481065}
10491066
10501067#[ inline]
@@ -1067,16 +1084,22 @@ pub(crate) fn truncate(n: Number, arena: &mut Arena) -> Number {
10671084 }
10681085}
10691086
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 ( ) ) ) ,
10741092 } ;
10751093
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 ) ;
10781099
1079- Ok ( floor ( result, arena) )
1100+ machine_st. error_form ( eval_error, stub)
1101+ } )
1102+ } )
10801103}
10811104
10821105pub ( crate ) fn bitwise_complement ( n1 : Number , arena : & mut Arena ) -> Result < Number , MachineStubGen > {
0 commit comments