1
- /* Copyright 2016 MongoDB Inc.
1
+ /* Copyright 2016-2017 MongoDB Inc.
2
2
*
3
3
* Licensed under the Apache License, Version 2.0 (the "License");
4
4
* you may not use this file except in compliance with the License.
@@ -37,7 +37,9 @@ public struct Decimal128 : IConvertible, IComparable<Decimal128>, IEquatable<Dec
37
37
private const short __maxSignificandDigits = 34 ;
38
38
39
39
// private static fields
40
- private static readonly UInt128 __maxSignificand = UInt128 . Parse ( "9999999999999999999999999999999999" ) ;
40
+ private static readonly UInt128 __maxSignificand = UInt128 . Parse ( "9999999999999999999999999999999999" ) ; // must be initialized before Decimal128.Parse is called
41
+ private static readonly Decimal128 __maxDecimalValue = Decimal128 . Parse ( "79228162514264337593543950335" ) ;
42
+ private static readonly Decimal128 __minDecimalValue = Decimal128 . Parse ( "-79228162514264337593543950335" ) ;
41
43
private static readonly Decimal128 __maxValue = Decimal128 . Parse ( "9999999999999999999999999999999999E+6111" ) ;
42
44
private static readonly Decimal128 __minValue = Decimal128 . Parse ( "-9999999999999999999999999999999999E+6111" ) ;
43
45
@@ -714,44 +716,62 @@ public static decimal ToDecimal(Decimal128 d)
714
716
{
715
717
if ( Flags . IsFirstForm ( d . _highBits ) )
716
718
{
717
- var exponent = Decimal128 . GetExponent ( d ) ;
718
-
719
- // try to get the exponent within the range of 0 to -28
720
- if ( exponent > 0 )
719
+ if ( Decimal128 . IsZero ( d ) )
721
720
{
722
- d = Decimal128 . DecreaseExponent ( d , 0 ) ;
723
- exponent = Decimal128 . GetExponent ( d ) ;
721
+ return decimal . Zero ;
724
722
}
725
- else if ( exponent < - 28 )
723
+ else if ( Decimal128 . Compare ( d , __minDecimalValue ) < 0 || Decimal128 . Compare ( d , __maxDecimalValue ) > 0 )
726
724
{
727
- d = Decimal128 . IncreaseExponent ( d , - 28 ) ;
728
- exponent = Decimal128 . GetExponent ( d ) ;
725
+ throw new OverflowException ( "Value is too large or too small to be converted to a Decimal." ) ;
729
726
}
730
727
731
- // try to get the significand to have zeros for the high order 32 bits
728
+ var isNegative = Decimal128 . IsNegative ( d ) ;
729
+ var exponent = Decimal128 . GetExponent ( d ) ;
732
730
var significand = Decimal128 . GetSignificand ( d ) ;
731
+
732
+ // decimal significand must fit in 96 bits
733
733
while ( ( significand . High >> 32 ) != 0 )
734
734
{
735
- uint remainder ;
736
- var significandDividedBy10 = UInt128 . Divide ( significand , ( uint ) 10 , out remainder ) ;
737
- if ( remainder != 0 )
735
+ uint remainder ; // ignored
736
+ significand = UInt128 . Divide ( significand , 10 , out remainder ) ;
737
+ exponent += 1 ;
738
+ }
739
+
740
+ // decimal exponents must be between 0 and -28
741
+ if ( exponent > 0 )
742
+ {
743
+ // bring exponent within range
744
+ while ( exponent > 0 )
738
745
{
739
- break ;
746
+ significand = UInt128 . Multiply ( significand , ( uint ) 10 ) ;
747
+ exponent -= 1 ;
740
748
}
741
- exponent += 1 ;
742
- significand = significandDividedBy10 ;
743
749
}
750
+ else if ( exponent < - 28 )
751
+ {
752
+ // check if exponent is too far out of range to possibly be brought within range
753
+ if ( exponent < - 56 )
754
+ {
755
+ return decimal . Zero ;
756
+ }
744
757
758
+ // bring exponent within range
759
+ while ( exponent < - 28 )
760
+ {
761
+ uint remainder ; // ignored
762
+ significand = UInt128 . Divide ( significand , ( uint ) 10 , out remainder ) ;
763
+ exponent += 1 ;
764
+ }
745
765
746
- if ( exponent < - 28 || exponent > 0 || ( significand . High >> 32 ) != 0 )
747
- {
748
- throw new OverflowException ( "Value is too large or too small to be converted to a Decimal." ) ;
766
+ if ( significand . Equals ( UInt128 . Zero ) )
767
+ {
768
+ return decimal . Zero ;
769
+ }
749
770
}
750
771
751
772
var lo = ( int ) significand . Low ;
752
773
var mid = ( int ) ( significand . Low >> 32 ) ;
753
774
var hi = ( int ) significand . High ;
754
- var isNegative = Decimal128 . IsNegative ( d ) ;
755
775
var scale = ( byte ) ( - exponent ) ;
756
776
757
777
return new decimal ( lo , mid , hi , isNegative , scale ) ;
@@ -1193,28 +1213,24 @@ private static string ClampOrRound(ref int exponent, string significandString)
1193
1213
return significandString ;
1194
1214
}
1195
1215
1196
- private static Decimal128 DecreaseExponent ( Decimal128 x , short goal )
1216
+ private static void TryDecreaseExponent ( ref UInt128 significand , ref short exponent , short goal )
1197
1217
{
1198
- if ( Decimal128 . IsZero ( x ) )
1218
+ if ( significand . Equals ( UInt128 . Zero ) )
1199
1219
{
1200
- // return a zero with the desired exponent
1201
- return Decimal128 . FromComponents ( Decimal128 . IsNegative ( x ) , goal , UInt128 . Zero ) ;
1220
+ exponent = goal ;
1221
+ return ;
1202
1222
}
1203
1223
1204
- var exponent = GetExponent ( x ) ;
1205
- var significand = GetSignificand ( x ) ;
1206
1224
while ( exponent > goal )
1207
1225
{
1208
1226
var significandTimes10 = UInt128 . Multiply ( significand , ( uint ) 10 ) ;
1209
- if ( significandTimes10 . CompareTo ( Decimal128 . __maxSignificand ) <= 0 )
1227
+ if ( significandTimes10 . CompareTo ( Decimal128 . __maxSignificand ) > 0 )
1210
1228
{
1211
1229
break ;
1212
1230
}
1213
1231
exponent -= 1 ;
1214
1232
significand = significandTimes10 ;
1215
1233
}
1216
-
1217
- return Decimal128 . FromComponents ( Decimal128 . IsNegative ( x ) , exponent , significand ) ;
1218
1234
}
1219
1235
1220
1236
private static Decimal128 FromComponents ( bool isNegative , short exponent , UInt128 significand )
@@ -1243,16 +1259,14 @@ private static UInt128 GetSignificand(Decimal128 d)
1243
1259
return new UInt128 ( GetSignificandHighBits ( d ) , GetSignificandLowBits ( d ) ) ;
1244
1260
}
1245
1261
1246
- private static Decimal128 IncreaseExponent ( Decimal128 x , short goal )
1262
+ private static void TryIncreaseExponent ( ref UInt128 significand , ref short exponent , short goal )
1247
1263
{
1248
- if ( Decimal128 . IsZero ( x ) )
1264
+ if ( significand . Equals ( UInt128 . Zero ) )
1249
1265
{
1250
- // return a zero with the desired exponent
1251
- return Decimal128 . FromComponents ( Decimal128 . IsNegative ( x ) , goal , UInt128 . Zero ) ;
1266
+ exponent = goal ;
1267
+ return ;
1252
1268
}
1253
1269
1254
- var exponent = GetExponent ( x ) ;
1255
- var significand = GetSignificand ( x ) ;
1256
1270
while ( exponent < goal )
1257
1271
{
1258
1272
uint remainder ;
@@ -1264,8 +1278,6 @@ private static Decimal128 IncreaseExponent(Decimal128 x, short goal)
1264
1278
exponent += 1 ;
1265
1279
significand = significandDividedBy10 ;
1266
1280
}
1267
-
1268
- return Decimal128 . FromComponents ( Decimal128 . IsNegative ( x ) , exponent , significand ) ;
1269
1281
}
1270
1282
1271
1283
private static short MapDecimal128BiasedExponentToExponent ( short biasedExponent )
@@ -1883,27 +1895,30 @@ private int CompareNegativeNumbers(Decimal128 x, Decimal128 y)
1883
1895
private int ComparePositiveNumbers ( Decimal128 x , Decimal128 y )
1884
1896
{
1885
1897
var xExponent = GetExponent ( x ) ;
1898
+ var xSignificand = GetSignificand ( x ) ;
1886
1899
var yExponent = GetExponent ( y ) ;
1900
+ var ySignificand = GetSignificand ( y ) ;
1901
+
1887
1902
var exponentDifference = Math . Abs ( xExponent - yExponent ) ;
1888
1903
if ( exponentDifference <= 66 )
1889
1904
{
1890
1905
// we may or may not be able to make the exponents equal but we won't know until we try
1891
1906
// but we do know we can't eliminate an exponent difference larger than 66
1892
1907
if ( xExponent < yExponent )
1893
1908
{
1894
- x = IncreaseExponent ( x , yExponent ) ;
1895
- y = DecreaseExponent ( y , xExponent ) ;
1909
+ TryIncreaseExponent ( ref xSignificand , ref xExponent , yExponent ) ;
1910
+ TryDecreaseExponent ( ref ySignificand , ref yExponent , xExponent ) ;
1896
1911
}
1897
1912
else if ( xExponent > yExponent )
1898
1913
{
1899
- x = DecreaseExponent ( x , yExponent ) ;
1900
- y = IncreaseExponent ( y , xExponent ) ;
1914
+ TryDecreaseExponent ( ref xSignificand , ref xExponent , yExponent ) ;
1915
+ TryIncreaseExponent ( ref ySignificand , ref yExponent , xExponent ) ;
1901
1916
}
1902
1917
}
1903
1918
1904
1919
if ( xExponent == yExponent )
1905
1920
{
1906
- return GetSignificand ( x ) . CompareTo ( GetSignificand ( y ) ) ;
1921
+ return xSignificand . CompareTo ( ySignificand ) ;
1907
1922
}
1908
1923
else
1909
1924
{
0 commit comments