1- /* Copyright 2016 MongoDB Inc.
1+ /* Copyright 2016-2017 MongoDB Inc.
22*
33* Licensed under the Apache License, Version 2.0 (the "License");
44* you may not use this file except in compliance with the License.
@@ -37,7 +37,9 @@ public struct Decimal128 : IConvertible, IComparable<Decimal128>, IEquatable<Dec
3737 private const short __maxSignificandDigits = 34 ;
3838
3939 // 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" ) ;
4143 private static readonly Decimal128 __maxValue = Decimal128 . Parse ( "9999999999999999999999999999999999E+6111" ) ;
4244 private static readonly Decimal128 __minValue = Decimal128 . Parse ( "-9999999999999999999999999999999999E+6111" ) ;
4345
@@ -714,44 +716,62 @@ public static decimal ToDecimal(Decimal128 d)
714716 {
715717 if ( Flags . IsFirstForm ( d . _highBits ) )
716718 {
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 ) )
721720 {
722- d = Decimal128 . DecreaseExponent ( d , 0 ) ;
723- exponent = Decimal128 . GetExponent ( d ) ;
721+ return decimal . Zero ;
724722 }
725- else if ( exponent < - 28 )
723+ else if ( Decimal128 . Compare ( d , __minDecimalValue ) < 0 || Decimal128 . Compare ( d , __maxDecimalValue ) > 0 )
726724 {
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." ) ;
729726 }
730727
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 ) ;
732730 var significand = Decimal128 . GetSignificand ( d ) ;
731+
732+ // decimal significand must fit in 96 bits
733733 while ( ( significand . High >> 32 ) != 0 )
734734 {
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 )
738745 {
739- break ;
746+ significand = UInt128 . Multiply ( significand , ( uint ) 10 ) ;
747+ exponent -= 1 ;
740748 }
741- exponent += 1 ;
742- significand = significandDividedBy10 ;
743749 }
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+ }
744757
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+ }
745765
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+ }
749770 }
750771
751772 var lo = ( int ) significand . Low ;
752773 var mid = ( int ) ( significand . Low >> 32 ) ;
753774 var hi = ( int ) significand . High ;
754- var isNegative = Decimal128 . IsNegative ( d ) ;
755775 var scale = ( byte ) ( - exponent ) ;
756776
757777 return new decimal ( lo , mid , hi , isNegative , scale ) ;
@@ -1193,28 +1213,24 @@ private static string ClampOrRound(ref int exponent, string significandString)
11931213 return significandString ;
11941214 }
11951215
1196- private static Decimal128 DecreaseExponent ( Decimal128 x , short goal )
1216+ private static void TryDecreaseExponent ( ref UInt128 significand , ref short exponent , short goal )
11971217 {
1198- if ( Decimal128 . IsZero ( x ) )
1218+ if ( significand . Equals ( UInt128 . Zero ) )
11991219 {
1200- // return a zero with the desired exponent
1201- return Decimal128 . FromComponents ( Decimal128 . IsNegative ( x ) , goal , UInt128 . Zero ) ;
1220+ exponent = goal ;
1221+ return ;
12021222 }
12031223
1204- var exponent = GetExponent ( x ) ;
1205- var significand = GetSignificand ( x ) ;
12061224 while ( exponent > goal )
12071225 {
12081226 var significandTimes10 = UInt128 . Multiply ( significand , ( uint ) 10 ) ;
1209- if ( significandTimes10 . CompareTo ( Decimal128 . __maxSignificand ) <= 0 )
1227+ if ( significandTimes10 . CompareTo ( Decimal128 . __maxSignificand ) > 0 )
12101228 {
12111229 break ;
12121230 }
12131231 exponent -= 1 ;
12141232 significand = significandTimes10 ;
12151233 }
1216-
1217- return Decimal128 . FromComponents ( Decimal128 . IsNegative ( x ) , exponent , significand ) ;
12181234 }
12191235
12201236 private static Decimal128 FromComponents ( bool isNegative , short exponent , UInt128 significand )
@@ -1243,16 +1259,14 @@ private static UInt128 GetSignificand(Decimal128 d)
12431259 return new UInt128 ( GetSignificandHighBits ( d ) , GetSignificandLowBits ( d ) ) ;
12441260 }
12451261
1246- private static Decimal128 IncreaseExponent ( Decimal128 x , short goal )
1262+ private static void TryIncreaseExponent ( ref UInt128 significand , ref short exponent , short goal )
12471263 {
1248- if ( Decimal128 . IsZero ( x ) )
1264+ if ( significand . Equals ( UInt128 . Zero ) )
12491265 {
1250- // return a zero with the desired exponent
1251- return Decimal128 . FromComponents ( Decimal128 . IsNegative ( x ) , goal , UInt128 . Zero ) ;
1266+ exponent = goal ;
1267+ return ;
12521268 }
12531269
1254- var exponent = GetExponent ( x ) ;
1255- var significand = GetSignificand ( x ) ;
12561270 while ( exponent < goal )
12571271 {
12581272 uint remainder ;
@@ -1264,8 +1278,6 @@ private static Decimal128 IncreaseExponent(Decimal128 x, short goal)
12641278 exponent += 1 ;
12651279 significand = significandDividedBy10 ;
12661280 }
1267-
1268- return Decimal128 . FromComponents ( Decimal128 . IsNegative ( x ) , exponent , significand ) ;
12691281 }
12701282
12711283 private static short MapDecimal128BiasedExponentToExponent ( short biasedExponent )
@@ -1883,27 +1895,30 @@ private int CompareNegativeNumbers(Decimal128 x, Decimal128 y)
18831895 private int ComparePositiveNumbers ( Decimal128 x , Decimal128 y )
18841896 {
18851897 var xExponent = GetExponent ( x ) ;
1898+ var xSignificand = GetSignificand ( x ) ;
18861899 var yExponent = GetExponent ( y ) ;
1900+ var ySignificand = GetSignificand ( y ) ;
1901+
18871902 var exponentDifference = Math . Abs ( xExponent - yExponent ) ;
18881903 if ( exponentDifference <= 66 )
18891904 {
18901905 // we may or may not be able to make the exponents equal but we won't know until we try
18911906 // but we do know we can't eliminate an exponent difference larger than 66
18921907 if ( xExponent < yExponent )
18931908 {
1894- x = IncreaseExponent ( x , yExponent ) ;
1895- y = DecreaseExponent ( y , xExponent ) ;
1909+ TryIncreaseExponent ( ref xSignificand , ref xExponent , yExponent ) ;
1910+ TryDecreaseExponent ( ref ySignificand , ref yExponent , xExponent ) ;
18961911 }
18971912 else if ( xExponent > yExponent )
18981913 {
1899- x = DecreaseExponent ( x , yExponent ) ;
1900- y = IncreaseExponent ( y , xExponent ) ;
1914+ TryDecreaseExponent ( ref xSignificand , ref xExponent , yExponent ) ;
1915+ TryIncreaseExponent ( ref ySignificand , ref yExponent , xExponent ) ;
19011916 }
19021917 }
19031918
19041919 if ( xExponent == yExponent )
19051920 {
1906- return GetSignificand ( x ) . CompareTo ( GetSignificand ( y ) ) ;
1921+ return xSignificand . CompareTo ( ySignificand ) ;
19071922 }
19081923 else
19091924 {
0 commit comments