@@ -2051,7 +2051,8 @@ impl<S: Semantics> IeeeFloat<S> {
20512051 // Before rounding normalize the exponent of Category::Normal numbers.
20522052 let mut omsb = sig:: omsb ( & self . sig ) ;
20532053
2054- if omsb > 0 {
2054+ // Only skip this `if` if the value is exactly zero.
2055+ if omsb > 0 || loss != Loss :: ExactlyZero {
20552056 // OMSB is numbered from 1. We want to place it in the integer
20562057 // bit numbered PRECISION if possible, with a compensating change in
20572058 // the exponent.
@@ -3008,37 +3009,60 @@ mod sig {
30083009 // an addition or subtraction.
30093010 // Subtraction is more subtle than one might naively expect.
30103011 if * a_sign ^ b_sign {
3011- let loss;
3012-
3013- if bits == 0 {
3014- loss = Loss :: ExactlyZero ;
3012+ let ( mut loss, loss_is_from_b) = if bits == 0 {
3013+ ( Loss :: ExactlyZero , false )
30153014 } else if bits > 0 {
3016- loss = shift_right ( b_sig, & mut 0 , ( bits - 1 ) as usize ) ;
30173015 shift_left ( a_sig, a_exp, 1 ) ;
3016+ ( shift_right ( b_sig, & mut 0 , ( bits - 1 ) as usize ) , true )
30183017 } else {
3019- loss = shift_right ( a_sig, a_exp, ( -bits - 1 ) as usize ) ;
30203018 shift_left ( b_sig, & mut 0 , 1 ) ;
3021- }
3022-
3023- let borrow = ( loss != Loss :: ExactlyZero ) as Limb ;
3024-
3025- // Should we reverse the subtraction.
3026- if cmp ( a_sig, b_sig) == Ordering :: Less {
3027- // The code above is intended to ensure that no borrow is necessary.
3028- assert_eq ! ( sub( b_sig, a_sig, borrow) , 0 ) ;
3029- a_sig. copy_from_slice ( b_sig) ;
3030- * a_sign = !* a_sign;
3031- } else {
3032- // The code above is intended to ensure that no borrow is necessary.
3033- assert_eq ! ( sub( a_sig, b_sig, borrow) , 0 ) ;
3034- }
3019+ ( shift_right ( a_sig, a_exp, ( -bits - 1 ) as usize ) , false )
3020+ } ;
30353021
3036- // Invert the lost fraction - it was on the RHS and subtracted.
3037- match loss {
3022+ let invert_loss = |loss| match loss {
30383023 Loss :: LessThanHalf => Loss :: MoreThanHalf ,
30393024 Loss :: MoreThanHalf => Loss :: LessThanHalf ,
30403025 _ => loss,
3026+ } ;
3027+
3028+ // Should we reverse the subtraction.
3029+ match cmp ( a_sig, b_sig) {
3030+ Ordering :: Less => {
3031+ let borrow = if loss != Loss :: ExactlyZero && !loss_is_from_b {
3032+ // The loss is being subtracted, borrow from the significand and invert
3033+ // `loss`.
3034+ loss = invert_loss ( loss) ;
3035+ 1
3036+ } else {
3037+ 0
3038+ } ;
3039+ // The code above is intended to ensure that no borrow is necessary.
3040+ assert_eq ! ( sub( b_sig, a_sig, borrow) , 0 ) ;
3041+ a_sig. copy_from_slice ( b_sig) ;
3042+ * a_sign = !* a_sign;
3043+ }
3044+ Ordering :: Greater => {
3045+ let borrow = if loss != Loss :: ExactlyZero && loss_is_from_b {
3046+ // The loss is being subtracted, borrow from the significand and invert
3047+ // `loss`.
3048+ loss = invert_loss ( loss) ;
3049+ 1
3050+ } else {
3051+ 0
3052+ } ;
3053+ // The code above is intended to ensure that no borrow is necessary.
3054+ assert_eq ! ( sub( a_sig, b_sig, borrow) , 0 ) ;
3055+ }
3056+ Ordering :: Equal => {
3057+ a_sig. fill ( 0 ) ;
3058+ if loss != Loss :: ExactlyZero && loss_is_from_b {
3059+ // b is slightly larger due to the loss, flip the sign.
3060+ * a_sign = !* a_sign;
3061+ }
3062+ }
30413063 }
3064+
3065+ loss
30423066 } else {
30433067 let loss = if bits > 0 {
30443068 shift_right ( b_sig, & mut 0 , bits as usize )
@@ -3051,6 +3075,69 @@ mod sig {
30513075 }
30523076 }
30533077
3078+ #[ test]
3079+ fn test_add_or_sub ( ) {
3080+ #[ track_caller]
3081+ fn run_test (
3082+ subtract : bool ,
3083+ mut lhs_sign : bool ,
3084+ mut lhs_exponent : ExpInt ,
3085+ mut lhs_significand : Limb ,
3086+ rhs_sign : bool ,
3087+ rhs_exponent : ExpInt ,
3088+ mut rhs_significand : Limb ,
3089+ expected_sign : bool ,
3090+ expected_exponent : ExpInt ,
3091+ expected_significand : Limb ,
3092+ expected_loss : Loss ,
3093+ ) {
3094+ let loss = add_or_sub (
3095+ core:: array:: from_mut ( & mut lhs_significand) ,
3096+ & mut lhs_exponent,
3097+ & mut lhs_sign,
3098+ core:: array:: from_mut ( & mut rhs_significand) ,
3099+ rhs_exponent,
3100+ rhs_sign ^ subtract,
3101+ ) ;
3102+ assert_eq ! ( loss, expected_loss) ;
3103+ assert_eq ! ( lhs_sign, expected_sign) ;
3104+ assert_eq ! ( lhs_exponent, expected_exponent) ;
3105+ assert_eq ! ( lhs_significand, expected_significand) ;
3106+ }
3107+
3108+ // Test cases are all combinations of:
3109+ // {equal exponents, LHS larger exponent, RHS larger exponent}
3110+ // {equal significands, LHS larger significand, RHS larger significand}
3111+ // {no loss, loss}
3112+
3113+ // Equal exponents (loss cannot occur as their is no shifting)
3114+ run_test ( true , false , 1 , 0x10 , false , 1 , 0x5 , false , 1 , 0xb , Loss :: ExactlyZero ) ;
3115+ run_test ( false , false , -2 , 0x20 , true , -2 , 0x20 , false , -2 , 0 , Loss :: ExactlyZero ) ;
3116+ run_test ( false , true , 3 , 0x20 , false , 3 , 0x30 , false , 3 , 0x10 , Loss :: ExactlyZero ) ;
3117+
3118+ // LHS larger exponent
3119+ // LHS significand greater after shitfing
3120+ run_test ( true , false , 7 , 0x100 , false , 3 , 0x100 , false , 6 , 0x1e0 , Loss :: ExactlyZero ) ;
3121+ run_test ( true , false , 7 , 0x100 , false , 3 , 0x101 , false , 6 , 0x1df , Loss :: MoreThanHalf ) ;
3122+ // Significands equal after shitfing
3123+ run_test ( true , false , 7 , 0x100 , false , 3 , 0x1000 , false , 6 , 0 , Loss :: ExactlyZero ) ;
3124+ run_test ( true , false , 7 , 0x100 , false , 3 , 0x1001 , true , 6 , 0 , Loss :: LessThanHalf ) ;
3125+ // RHS significand greater after shitfing
3126+ run_test ( true , false , 7 , 0x100 , false , 3 , 0x10000 , true , 6 , 0x1e00 , Loss :: ExactlyZero ) ;
3127+ run_test ( true , false , 7 , 0x100 , false , 3 , 0x10001 , true , 6 , 0x1e00 , Loss :: LessThanHalf ) ;
3128+
3129+ // RHS larger exponent
3130+ // RHS significand greater after shitfing
3131+ run_test ( true , false , 3 , 0x100 , false , 7 , 0x100 , true , 6 , 0x1e0 , Loss :: ExactlyZero ) ;
3132+ run_test ( true , false , 3 , 0x101 , false , 7 , 0x100 , true , 6 , 0x1df , Loss :: MoreThanHalf ) ;
3133+ // Significands equal after shitfing
3134+ run_test ( true , false , 3 , 0x1000 , false , 7 , 0x100 , false , 6 , 0 , Loss :: ExactlyZero ) ;
3135+ run_test ( true , false , 3 , 0x1001 , false , 7 , 0x100 , false , 6 , 0 , Loss :: LessThanHalf ) ;
3136+ // LHS significand greater after shitfing
3137+ run_test ( true , false , 3 , 0x10000 , false , 7 , 0x100 , false , 6 , 0x1e00 , Loss :: ExactlyZero ) ;
3138+ run_test ( true , false , 3 , 0x10001 , false , 7 , 0x100 , false , 6 , 0x1e00 , Loss :: LessThanHalf ) ;
3139+ }
3140+
30543141 /// `[low, high] = a * b`.
30553142 ///
30563143 /// This cannot overflow, because
0 commit comments