@@ -12,7 +12,7 @@ mod math {
1212 } ;
1313 use itertools:: Itertools ;
1414 use malachite_bigint:: BigInt ;
15- use num_traits:: { One , Signed , Zero } ;
15+ use num_traits:: { One , Signed , ToPrimitive , Zero } ;
1616 use rustpython_common:: { float_ops, int:: true_div} ;
1717 use std:: cmp:: Ordering ;
1818
@@ -563,11 +563,65 @@ mod math {
563563
564564 if value == 0_f64 || !value. is_finite ( ) {
565565 // NaNs, zeros and infinities are returned unchanged
566- Ok ( value)
566+ return Ok ( value) ;
567+ }
568+
569+ // Using IEEE 754 bit manipulation to handle large exponents correctly.
570+ // Direct multiplication would overflow for large i values, especially when computing
571+ // the largest finite float (i=1024, x<1.0). By directly modifying the exponent bits,
572+ // we avoid intermediate overflow to infinity.
573+ let ( mant, exp0) = float_ops:: decompose_float ( value) ;
574+
575+ let i_big = i. as_bigint ( ) ;
576+ let overflow_bound = BigInt :: from ( 1024_i32 - exp0) ; // i > 1024 - exp0 => overflow
577+ if i_big > & overflow_bound {
578+ return Err ( vm. new_overflow_error ( "math range error" ) ) ;
579+ }
580+ if i_big == & overflow_bound && mant == 1.0 {
581+ return Err ( vm. new_overflow_error ( "math range error" ) ) ;
582+ }
583+ let underflow_bound = BigInt :: from ( -1074_i32 - exp0) ; // i < -1074 - exp0 => 0.0 with sign
584+ if i_big < & underflow_bound {
585+ return Ok ( 0.0f64 . copysign ( value) ) ;
586+ }
587+
588+ let i_small: i32 = i_big
589+ . to_i32 ( )
590+ . expect ( "exponent within [-1074-exp0, 1024-exp0] must fit in i32" ) ;
591+ let exp = exp0 + i_small;
592+
593+ const SIGN_MASK : u64 = 0x8000_0000_0000_0000 ;
594+ const FRAC_MASK : u64 = 0x000F_FFFF_FFFF_FFFF ;
595+ let sign_bit: u64 = if value. is_sign_negative ( ) {
596+ SIGN_MASK
567597 } else {
568- let result = value * ( 2_f64 ) . powf ( try_bigint_to_f64 ( i. as_bigint ( ) , vm) ?) ;
569- result_or_overflow ( value, result, vm)
598+ 0
599+ } ;
600+ let mant_bits = mant. to_bits ( ) & FRAC_MASK ;
601+ if exp >= -1021 {
602+ let e_bits = ( 1022_i32 + exp) as u64 ;
603+ let result_bits = sign_bit | ( e_bits << 52 ) | mant_bits;
604+ return Ok ( f64:: from_bits ( result_bits) ) ;
570605 }
606+
607+ let full_mant: u64 = ( 1u64 << 52 ) | mant_bits;
608+ let shift: u32 = ( -exp - 1021 ) as u32 ;
609+ let frac_shifted = full_mant >> shift;
610+ let lost_bits = full_mant & ( ( 1u64 << shift) - 1 ) ;
611+
612+ let half = 1u64 << ( shift - 1 ) ;
613+ let frac = if ( lost_bits > half) || ( lost_bits == half && ( frac_shifted & 1 ) == 1 ) {
614+ frac_shifted + 1
615+ } else {
616+ frac_shifted
617+ } ;
618+
619+ let result_bits = if frac >= ( 1u64 << 52 ) {
620+ sign_bit | ( 1u64 << 52 )
621+ } else {
622+ sign_bit | frac
623+ } ;
624+ Ok ( f64:: from_bits ( result_bits) )
571625 }
572626
573627 fn math_perf_arb_len_int_op < F > ( args : PosArgs < ArgIndex > , op : F , default : BigInt ) -> BigInt
0 commit comments