@@ -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