LargeDecimal Class
Life With Nate
Nate's Poetry Page
Resume (PDF)
Nate's Code
ASP.NET 2.0/3.5
VB.NET 2.0/3.5
BitStringConverter
Fraction
LargeDecimal
LDFraction
Transparency
Just For Fun
Windows Phone 7
Description
The LargeDecimal class is used to store very large (or very small) decimal values. This is done by storing a String containing the digits (with the decimal point and leading and trailing zeroes removed) and a power of ten as an Integer.
Example
LargeDecimal.PlacesCalculated:
largedecimal1:
largedecimal2:
largedecimal1.ToString()
largedecimal2.ToString()
largedecimal1.Absolute()
largedecimal2.Absolute()
largedecimal1.IsIntegral()
largedecimal2.IsIntegral()
largedecimal1.Truncate()
largedecimal2.Truncate()
largedecimal1.Truncate(3)
largedecimal2.Truncate(3)
-largedecimal1
-largedecimal2
largedecimal1 + largedecimal2 =
largedecimal1 < largedecimal2 =
largedecimal1 - largedecimal2 =
largedecimal2 < largedecimal1 =
largedecimal2 - largedecimal1 =
largedecimal1 > largedecimal2 =
largedecimal1 * largedecimal2 =
largedecimal2 > largedecimal1 =
largedecimal1 / largedecimal2 =
largedecimal1 = largedecimal2 =
largedecimal2 / largedecimal1 =
largedecimal1 <> largedecimal2 =
largedecimal1 Mod largedecimal2 =
largedecimal1 <= largedecimal2 =
largedecimal2 Mod largedecimal1 =
largedecimal2 <= largedecimal1 =
largedecimal1 >= largedecimal2 =
largedecimal2 >= largedecimal1 =
Properties & Methods
LargeDecimal
Digits
- A String containing the digits that are used when calculating or displaying values. When this has a value of String.Empty, the value is 0
Exponent
- An Integer which is the power that 10 is raised to when calculating or displaying values
Sign
- An Integer specifying whether the value is positive or negative
PlacesCalculated
- An Integer specifying the number of decimal places to calculate when doing division using the / operator, default is 10. This is a shared property for all instances of the LargeDecimal class
IsIntegral
- A ReadOnly Boolean value that indicates whether the value is an integer
LargeDecimal()
Constructor that creates a LargeDecimal with a value of 0
LargeDecimal(value As String)
Constructor that accepts a decimal value as a string
value
- Any String that can be converted to a decimal value
LargeDecimal(value As Decimal)
LargeDecimal(value As Double)
Constructor that creates a LargeDecimal from a value of type Decimal or Double
value
- The Decimal or Double whose value is to be used
LargeDecimal(sgn As Integer,dgt As String,exp As Integer)
Constructor that creates a large decimal from a String of digits and a power of 10
sgn
- An Integer specifying the sign
dgt
- A String of digits
exp
- The power to which 10 should be raised
ToString()
Displays the value in a standard decimal format
Truncate()
Truncate(3)
Returns a LargeDecimal with no more than the specified number of digits to the right of the decimal
Absolute()
Returns the absolute value as a LargeDecimal
Defined Operators:
+
- Addition
-
- Subtraction
*
- Multiplication
/
- Division
-
- Negation
<
- Less Than
>
- Greater Than
=
- Equality
<>
- Inequality
<=
- Less Than Or Equal To
>=
- Greater Than Or Equal To
Mod
- Modulus
Defined Type Conversions (narrowing):
LargeDecimal to SByte
LargeDecimal to Byte
LargeDecimal to Short
LargeDecimal to UShort
LargeDecimal to Integer
LargeDecimal to UInteger
LargeDecimal to Long
LargeDecimal to ULong
LargeDecimal to Decimal
LargeDecimal to Single
LargeDecimal to Double
Fraction to LargeDecimal
LargeDecimal to Fraction
LDFraction to LargeDecimal
Defined Type Conversions (widening):
Long to LargeDecimal
ULong to LargeDecimal
Decimal to LargeDecimal
Double to LargeDecimal
LargeDecimal to LDFraction
Source Code
LargeDecimal.vb:
Namespace NathanSokalski Public Class LargeDecimal Private Shared _placescalculated As Integer = 10 Private _digits As String = String.Empty Private _exponent As Integer = 0 Private _sign As Integer = 1 Public Shared Property PlacesCalculated() As Integer Get Return LargeDecimal._placescalculated End Get Set(ByVal value As Integer) LargeDecimal._placescalculated = Math.Max(0, value) End Set End Property Public Property Digits() As String Get Return Me._digits End Get Set(ByVal value As String) Me._digits = value End Set End Property Public Property Exponent() As Integer Get Return Me._exponent End Get Set(ByVal value As Integer) Me._exponent = value End Set End Property Public Property Sign() As Integer Get Return Me._sign End Get Set(ByVal value As Integer) Me._sign = Math.Sign(value) End Set End Property Public ReadOnly Property IsIntegral() As Boolean Get Return Me.Exponent >= 0 End Get End Property Public Sub New() Me.Digits = String.Empty Me.Exponent = 0 Me.Sign = 1 End Sub Public Sub New(ByVal value As String) Dim newvalue As String = value.Trim(" "c).TrimStart("-"c).TrimStart("0"c).TrimEnd("."c) If newvalue.Contains(".") Then 'Value is a potential decimal newvalue = newvalue.TrimEnd("0"c) If newvalue.EndsWith(".") Then 'Value is an integer newvalue = newvalue.TrimEnd("."c) Me.Digits = newvalue.TrimEnd("0"c) Me.Exponent = newvalue.Length - Me.Digits.Length Else 'Value is a decimal Me.Digits = newvalue.Replace(".", String.Empty) Me.Exponent = -(newvalue.Length - 1 - newvalue.IndexOf("."c)) End If Else 'Value is an integer Me.Digits = newvalue.TrimEnd("0"c) Me.Exponent = newvalue.Length - Me.Digits.Length End If If value.StartsWith("-") Then Me.Sign = -1 End Sub Public Sub New(ByVal value As Decimal) Me.New(value.ToString()) End Sub Public Sub New(ByVal value As Double) Me.New(value.ToString()) End Sub Public Sub New(ByVal sgn As Integer, ByVal dgt As String, ByVal exp As Integer) If System.Text.RegularExpressions.Regex.IsMatch(dgt, "\s*\d*\s*") Then Dim tempdigits As String = dgt.Trim(" "c).TrimStart("-"c).TrimStart("0"c).Replace(".", String.Empty) Dim tempexp As Integer = exp While tempdigits.EndsWith("0") tempdigits = tempdigits.Remove(tempdigits.LastIndexOf("0"c), 1) tempexp += 1 End While Me.Digits = tempdigits Me.Exponent = tempexp Me.Sign = sgn Else Throw New System.ArgumentException("You have entered an invalid value for the Digits property", "dgt") End If End Sub Public Overrides Function ToString() As String Dim temp As String = String.Empty If Me.Sign = -1 Then temp = "-" If String.IsNullOrEmpty(Me.Digits) Then temp = "0" ElseIf Me.Exponent = 0 Then temp = String.Concat(temp, Me.Digits) ElseIf Me.Exponent > 0 Then temp = String.Concat(temp, Me.Digits) temp = temp.PadRight(temp.Length + Me.Exponent, "0"c) ElseIf (Me.Exponent + Me.Digits.Length) = 0 Then temp = String.Concat(temp, String.Concat("0.", Me.Digits)) ElseIf Me.Exponent < 0 AndAlso (Me.Exponent + Me.Digits.Length) > 0 Then temp = String.Concat(temp, Me.Digits.Insert(Me.Exponent + Me.Digits.Length, ".")) ElseIf (Me.Exponent + Me.Digits.Length) < 0 Then temp = String.Concat(temp, "0.", Me.Digits.PadLeft(-Me.Exponent, "0"c)) End If Return temp End Function Public Function Truncate() As LargeDecimal If Me.IsIntegral() Then Return Me Else Return New LargeDecimal(Me.Sign, Me.ToString().Split("."c)(0), 0) End Function Public Function Truncate(ByVal precision As Integer) As LargeDecimal If Me.IsIntegral() OrElse Me.Exponent + precision > 0 Then Return Me Else Return New LargeDecimal(Me.ToString().Substring(0, Me.ToString().IndexOf("."c) + 1 + precision)) End If End Function Public Function Absolute() As LargeDecimal Return New LargeDecimal(1, Me.Digits, Me.Exponent) End Function Private Shared Function AddPositive(ByVal num1 As String, ByVal num2 As String) As String Dim result As String = String.Empty Dim carried, tempresult As Byte If Not num1.Contains(".") Then num1 = String.Concat(num1, ".") If Not num2.Contains(".") Then num2 = String.Concat(num2, ".") If num1.IndexOf("."c) < num2.IndexOf("."c) Then num1 = num1.PadLeft(num1.Length + num2.IndexOf("."c) - num1.IndexOf("."c), "0"c) ElseIf num1.IndexOf("."c) > num2.IndexOf("."c) Then num2 = num2.PadLeft(num2.Length + num1.IndexOf("."c) - num2.IndexOf("."c), "0"c) End If If num1.Length > num2.Length Then num2 = num2.PadRight(num1.Length, "0"c) Else num1 = num1.PadRight(num2.Length, "0"c) Dim decimalindex As Integer = num1.IndexOf("."c) num1 = num1.Replace(".", String.Empty) : num2 = num2.Replace(".", String.Empty) For i As Integer = num1.Length - 1 To 0 Step -1 tempresult = CByte(Val(num1(i)) + Val(num2(i)) + carried) If tempresult >= 10 Then carried = 1 result = result.Insert(0, CStr(tempresult - 10)) Else carried = 0 result = result.Insert(0, CStr(tempresult)) End If Next result = result.Insert(decimalindex, ".").TrimEnd("."c) If carried = 1 Then result = result.Insert(0, "1") Return result End Function Private Shared Function SubtractPositive(ByVal num1 As LargeDecimal, ByVal num2 As LargeDecimal) As LargeDecimal Dim isnegative As Boolean Dim borrowed As Byte = 0 Dim result As String = String.Empty Dim temp1, temp2 As String If num1 >= num2 Then isnegative = False temp1 = num1.ToString() temp2 = num2.ToString() Else isnegative = True temp1 = num2.ToString() temp2 = num1.ToString() End If If Not temp1.Contains(".") Then temp1 = String.Concat(temp1, ".") If Not temp2.Contains(".") Then temp2 = String.Concat(temp2, ".") If temp1.IndexOf("."c) < temp2.IndexOf("."c) Then temp1 = temp1.PadLeft(temp1.Length + temp2.IndexOf("."c) - temp1.IndexOf("."c), "0"c) ElseIf temp1.IndexOf("."c) > temp2.IndexOf("."c) Then temp2 = temp2.PadLeft(temp2.Length + temp1.IndexOf("."c) - temp2.IndexOf("."c), "0"c) End If If temp1.Length > temp2.Length Then temp2 = temp2.PadRight(temp1.Length, "0"c) Else temp1 = temp1.PadRight(temp2.Length, "0"c) Dim decimalindex As Integer = temp1.IndexOf("."c) temp1 = temp1.Replace(".", String.Empty) : temp2 = temp2.Replace(".", String.Empty) For i As Integer = temp1.Length - 1 To 0 Step -1 If (Val(temp2(i)) + borrowed) > Val(temp1(i)) Then result = result.Insert(0, CStr(Val(temp1(i)) + 10 - Val(temp2(i)) - borrowed)) borrowed = 1 Else result = result.Insert(0, CStr(Val(temp1(i)) - Val(temp2(i)) - borrowed)) borrowed = 0 End If Next result = result.Insert(decimalindex, ".") If isnegative Then result = result.Insert(0, "-") Return New LargeDecimal(result) End Function Private Shared Function GTEPosInt(ByVal num1 As String, ByVal num2 As String) As Boolean If num1.Length > num2.Length Then Return True ElseIf num2.Length > num1.Length Then Return False Else For i As Integer = 0 To num1.Length - 1 If num1(i) <> num2(i) Then Return Val(num1(i)) > Val(num2(i)) Next Return True End If End Function Public Shared Operator +(ByVal num1 As LargeDecimal, ByVal num2 As LargeDecimal) As LargeDecimal If num1.Sign = -1 Then If num2.Sign = -1 Then Return New LargeDecimal(String.Concat("-", LargeDecimal.AddPositive((-num1).ToString(), (-num2).ToString()))) Else Return num2 - -num1 End If Else If num2.Sign = -1 Then Return num1 - -num2 Else Return New LargeDecimal(LargeDecimal.AddPositive(num1.ToString(), num2.ToString())) End If End If End Operator Public Shared Operator -(ByVal num1 As LargeDecimal, ByVal num2 As LargeDecimal) As LargeDecimal If num1.Sign = -1 Then If num2.Sign = -1 Then Return LargeDecimal.SubtractPositive(-num2, -num1) Else Return num1 + -num2 End If Else If num2.Sign = -1 Then Return num1 + -num2 Else Return LargeDecimal.SubtractPositive(num1, num2) End If End If End Operator Public Shared Operator *(ByVal num1 As LargeDecimal, ByVal num2 As LargeDecimal) As LargeDecimal Dim digitsresult As String = "0" Dim tempresult As String = String.Empty Dim temp As Integer Dim carried As Integer = 0 Dim digits1 As String = num1.Digits Dim digits2 As String = num2.Digits For i As Integer = digits2.Length - 1 To 0 Step -1 For j As Integer = digits1.Length - 1 To 0 Step -1 temp = Val(digits2(i)) * Val(digits1(j)) + carried tempresult = tempresult.Insert(0, CStr(temp Mod 10)) carried = CInt((temp - (temp Mod 10)) / 10) Next If carried > 0 Then tempresult = String.Concat(carried, tempresult) carried = 0 digitsresult = LargeDecimal.AddPositive(digitsresult, tempresult.PadRight(tempresult.Length + digits2.Length - i - 1, "0"c)) tempresult = String.Empty Next Return New LargeDecimal(num1.Sign * num2.Sign, digitsresult, num1.Exponent + num2.Exponent) End Operator Public Shared Operator /(ByVal num1 As LargeDecimal, ByVal num2 As LargeDecimal) As LargeDecimal If num2 = 0 Then : Throw New System.DivideByZeroException() ElseIf num1 = 0 OrElse num2 = 1 Then : Return num1 ElseIf num1 = num2 Then : Return 1 Else Dim tempnum As New LargeDecimal(1, num1.Digits, num1.Exponent) Dim tempdenom As New LargeDecimal(1, num2.Digits, num2.Exponent) 'Convert both values to integers If Not (num1.IsIntegral() AndAlso num2.IsIntegral()) Then Dim temp As Integer = -Math.Min(num1.Exponent, num2.Exponent) tempnum.Exponent += temp tempdenom.Exponent += temp End If Dim result As New System.Text.StringBuilder() Dim numdigits As New Queue(Of Char)(tempnum.ToString().ToCharArray()) tempnum = 0 If num1.Sign <> num2.Sign Then result.Append("-") 'Calculate the whole part of the result While numdigits.Count > 0 tempnum.Digits &= numdigits.Dequeue() For i As Integer = 1 To 10 If (i * tempdenom) > tempnum Then result.Append(i - 1) tempnum -= (i - 1) * tempdenom Exit For End If Next End While 'Calculate the fractional part of the result, if necessary If LargeDecimal.PlacesCalculated > 0 Then result.Append(".") For i As Integer = 1 To LargeDecimal.PlacesCalculated tempnum.Digits &= "0" For j As Integer = 1 To 10 If (j * tempdenom) > tempnum Then result.Append(j - 1) tempnum -= (j - 1) * tempdenom Exit For End If Next Next End If Return New LargeDecimal(result.ToString()) End If End Operator Public Shared Operator -(ByVal num As LargeDecimal) As LargeDecimal If num.Digits = String.Empty Then Return num Else Return New LargeDecimal(-num.Sign, num.Digits, num.Exponent) End Operator Public Shared Operator <(ByVal num1 As LargeDecimal, ByVal num2 As LargeDecimal) As Boolean Dim temp1 As String = num1.ToString() Dim temp2 As String = num2.ToString() If temp1.StartsWith("-") AndAlso Not temp2.StartsWith("-") Then Return True ElseIf temp2.StartsWith("-") AndAlso Not temp1.StartsWith("-") Then Return False Else Dim negative As Boolean = temp1.StartsWith("-") temp1 = temp1.TrimStart("-"c) : temp2 = temp2.TrimStart("-"c) If Not temp1.Contains(".") Then temp1 = String.Concat(temp1, ".") If Not temp2.Contains(".") Then temp2 = String.Concat(temp2, ".") If temp1.IndexOf("."c) < temp2.IndexOf("."c) Then temp1 = temp1.PadLeft(temp1.Length + temp2.IndexOf("."c) - temp1.IndexOf("."c), "0"c) ElseIf temp1.IndexOf("."c) > temp2.IndexOf("."c) Then temp2 = temp2.PadLeft(temp2.Length + temp1.IndexOf("."c) - temp2.IndexOf("."c), "0"c) End If If temp1.Length > temp2.Length Then temp2 = temp2.PadRight(temp1.Length, "0"c) Else temp1 = temp1.PadRight(temp2.Length, "0"c) temp1 = temp1.Replace(".", String.Empty) : temp2 = temp2.Replace(".", String.Empty) For i As Integer = 0 To temp1.Length - 1 If temp1(i) <> temp2(i) Then Return Val(temp1(i)) < Val(temp2(i)) Xor negative Next Return False End If End Operator Public Shared Operator >(ByVal num1 As LargeDecimal, ByVal num2 As LargeDecimal) As Boolean Dim temp1 As String = num1.ToString() Dim temp2 As String = num2.ToString() If temp1.StartsWith("-") AndAlso Not temp2.StartsWith("-") Then Return False ElseIf temp2.StartsWith("-") AndAlso Not temp1.StartsWith("-") Then Return True Else Dim negative As Boolean = temp1.StartsWith("-") temp1 = temp1.TrimStart("-"c) : temp2 = temp2.TrimStart("-"c) If Not temp1.Contains(".") Then temp1 = String.Concat(temp1, ".") If Not temp2.Contains(".") Then temp2 = String.Concat(temp2, ".") If temp1.IndexOf("."c) < temp2.IndexOf("."c) Then temp1 = temp1.PadLeft(temp1.Length + temp2.IndexOf("."c) - temp1.IndexOf("."c), "0"c) ElseIf temp1.IndexOf("."c) > temp2.IndexOf("."c) Then temp2 = temp2.PadLeft(temp2.Length + temp1.IndexOf("."c) - temp2.IndexOf("."c), "0"c) End If If temp1.Length > temp2.Length Then temp2 = temp2.PadRight(temp1.Length, "0"c) Else temp1 = temp1.PadRight(temp2.Length, "0"c) temp1 = temp1.Replace(".", String.Empty) : temp2 = temp2.Replace(".", String.Empty) For i As Integer = 0 To temp1.Length - 1 If temp1(i) <> temp2(i) Then Return Val(temp1(i)) > Val(temp2(i)) Xor negative Next Return False End If End Operator Public Shared Operator =(ByVal num1 As LargeDecimal, ByVal num2 As LargeDecimal) As Boolean Return num1.ToString() = num2.ToString() End Operator Public Shared Operator <>(ByVal num1 As LargeDecimal, ByVal num2 As LargeDecimal) As Boolean Return num1.ToString() <> num2.ToString() End Operator Public Shared Operator <=(ByVal num1 As LargeDecimal, ByVal num2 As LargeDecimal) As Boolean Return num1 < num2 OrElse num1.ToString() = num2.ToString() End Operator Public Shared Operator >=(ByVal num1 As LargeDecimal, ByVal num2 As LargeDecimal) As Boolean Return num1 > num2 OrElse num1.ToString() = num2.ToString() End Operator Public Shared Operator Mod(ByVal num1 As LargeDecimal, ByVal num2 As LargeDecimal) As LargeDecimal If num1.Exponent < 0 OrElse num2.Exponent < 0 Then If num1.Exponent < 0 Then Throw New System.ArgumentException("You may only use the Mod operator when both values are integers", "num1") If num2.Exponent < 0 Then Throw New System.ArgumentException("You may only use the Mod operator when both values are integers", "num2") Else Select Case num1 Case Is = num2 : Return 0 Case Is < num2 : Return num1 Case Is > num2 Dim temp As String = (num1 / num2).ToString() If temp.Contains(".") Then Return num1 - (num2 * New LargeDecimal(temp.Substring(0, temp.IndexOf(".")))) Else Return num1 - (num2 * New LargeDecimal(temp)) End If End Select End If Return Nothing End Operator Public Shared Narrowing Operator CType(ByVal ld As LargeDecimal) As SByte Return CSByte(ld.ToString().Split("."c)(0)) End Operator Public Shared Narrowing Operator CType(ByVal ld As LargeDecimal) As Byte Return CByte(ld.ToString().Split("."c)(0)) End Operator Public Shared Narrowing Operator CType(ByVal ld As LargeDecimal) As Short Return CShort(ld.ToString().Split("."c)(0)) End Operator Public Shared Narrowing Operator CType(ByVal ld As LargeDecimal) As UShort Return CUShort(ld.ToString().Split("."c)(0)) End Operator Public Shared Narrowing Operator CType(ByVal ld As LargeDecimal) As Integer Return CInt(ld.ToString().Split("."c)(0)) End Operator Public Shared Narrowing Operator CType(ByVal ld As LargeDecimal) As UInteger Return CUInt(ld.ToString().Split("."c)(0)) End Operator Public Shared Narrowing Operator CType(ByVal ld As LargeDecimal) As Long Return CLng(ld.ToString().Split("."c)(0)) End Operator Public Shared Narrowing Operator CType(ByVal ld As LargeDecimal) As ULong Return CULng(ld.ToString().Split("."c)(0)) End Operator Public Shared Narrowing Operator CType(ByVal ld As LargeDecimal) As Decimal Return CDec(ld.ToString()) End Operator Public Shared Narrowing Operator CType(ByVal ld As LargeDecimal) As Single Return CSng(ld.ToString()) End Operator Public Shared Narrowing Operator CType(ByVal ld As LargeDecimal) As Double Return CDbl(ld.ToString()) End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As LargeDecimal Return frac.Sign() * ((CType(frac.Numerator, LargeDecimal) / CType(frac.Denominator, LargeDecimal)) + frac.WholePart) End Operator Public Shared Narrowing Operator CType(ByVal ld As LargeDecimal) As Fraction Return New Fraction(ld.ToString()) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As LargeDecimal Return frac.Sign() * ((CType(frac.Numerator, LargeDecimal) / CType(frac.Denominator, LargeDecimal)) + frac.WholePart) End Operator Public Shared Widening Operator CType(ByVal value As Long) As LargeDecimal Return New LargeDecimal(value) End Operator Public Shared Widening Operator CType(ByVal value As ULong) As LargeDecimal Return New LargeDecimal(value) End Operator Public Shared Widening Operator CType(ByVal value As Decimal) As LargeDecimal Return New LargeDecimal(value) End Operator Public Shared Widening Operator CType(ByVal value As Double) As LargeDecimal Return New LargeDecimal(value) End Operator Public Shared Widening Operator CType(ByVal ld As LargeDecimal) As LDFraction Return New LDFraction(ld.ToString()) End Operator End Class End Namespace
Remarks
When using the division operator (/), values are always rounded towards 0. In other words, positive values are rounded down, and negative values are rounded up.