@@ -2051,7 +2051,8 @@ impl<S: Semantics> IeeeFloat<S> {
2051
2051
// Before rounding normalize the exponent of Category::Normal numbers.
2052
2052
let mut omsb = sig:: omsb ( & self . sig ) ;
2053
2053
2054
- if omsb > 0 {
2054
+ // Only skip this `if` if the value is exactly zero.
2055
+ if omsb > 0 || loss != Loss :: ExactlyZero {
2055
2056
// OMSB is numbered from 1. We want to place it in the integer
2056
2057
// bit numbered PRECISION if possible, with a compensating change in
2057
2058
// the exponent.
@@ -3008,37 +3009,60 @@ mod sig {
3008
3009
// an addition or subtraction.
3009
3010
// Subtraction is more subtle than one might naively expect.
3010
3011
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 )
3015
3014
} else if bits > 0 {
3016
- loss = shift_right ( b_sig, & mut 0 , ( bits - 1 ) as usize ) ;
3017
3015
shift_left ( a_sig, a_exp, 1 ) ;
3016
+ ( shift_right ( b_sig, & mut 0 , ( bits - 1 ) as usize ) , true )
3018
3017
} else {
3019
- loss = shift_right ( a_sig, a_exp, ( -bits - 1 ) as usize ) ;
3020
3018
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
+ } ;
3035
3021
3036
- // Invert the lost fraction - it was on the RHS and subtracted.
3037
- match loss {
3022
+ let invert_loss = |loss| match loss {
3038
3023
Loss :: LessThanHalf => Loss :: MoreThanHalf ,
3039
3024
Loss :: MoreThanHalf => Loss :: LessThanHalf ,
3040
3025
_ => 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
+ }
3041
3063
}
3064
+
3065
+ loss
3042
3066
} else {
3043
3067
let loss = if bits > 0 {
3044
3068
shift_right ( b_sig, & mut 0 , bits as usize )
@@ -3051,6 +3075,69 @@ mod sig {
3051
3075
}
3052
3076
}
3053
3077
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
+
3054
3141
/// `[low, high] = a * b`.
3055
3142
///
3056
3143
/// This cannot overflow, because
0 commit comments