LDFraction 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 LDFraction class is a variation of the
Fraction
class that uses the
LargeDecimal
type rather than ULong to store the values.
Example
fraction1:
fraction2:
fraction1.ToString()
fraction2.ToString()
fraction1.ToImproperString()
fraction2.ToImproperString()
fraction1.Absolute()
fraction2.Absolute()
-fraction1
-fraction2
fraction1 + fraction2 =
fraction1 < fraction2 =
fraction1 - fraction2 =
fraction2 < fraction1 =
fraction2 - fraction1 =
fraction1 > fraction2 =
fraction1 * fraction2 =
fraction2 > fraction1 =
fraction1 / fraction2 =
fraction1 = fraction2 =
fraction2 / fraction1 =
fraction1 <> fraction2 =
fraction1 <= fraction2 =
fraction2 <= fraction1 =
fraction1 >= fraction2 =
fraction2 >= fraction1 =
Properties & Methods
LDFraction
LDFraction is a class that allows you to store and perform basic mathematical functions on fractions.
Sign
- An integer specifying whether the fraction is positive or negative
WholePart
- The whole part of the fraction
Numerator
- The numerator of the fractional part of the fraction
Denominator
- The denominator of the fraction. If you attempt to assign a value of 0 to this property an exception will be thrown
FractionRegularExpression
- A string constant containing a regular expression that can be used to validate whether a string is a valid fraction
LDFraction()
Constructor that creates a fraction with a value of 0
LDFraction(frac As String)
Constructor that accepts a fraction as a string
frac
- A String that is of any of the following formats: [-]a b/c, [-]a, or [-]a/b
LDFraction(sign As Integer,intpart As LargeDecimal,num As LargeDecimal,denom As LargeDecimal)
LDFraction(intpart As LargeDecimal,num As LargeDecimal,denom As LargeDecimal)
LDFraction(num As LargeDecimal,denom As LargeDecimal)
Constructors that create a new LDFraction using a sign and integral values
sign
- An integer specifying whether the fraction is positive or negative. Even though any integer will be accepted, only the sign will be stored. The overloads that do not use this parameter assume the input is positive and use the absolute value of the other parameters
intpart
- The whole part of the fraction
num
- The numerator of the fractional part of the fraction. If this is larger than the denominator, it will be adjusted along with the whole part
denom
- The denominator of the fraction
LDFraction(value As Decimal)
LDFraction(value As Double)
Constructor that creates an LDFraction from a value of type Decimal or Double
value
- A value of type Decimal or Double. Please keep in mind that the value used is the value returned by the Decimal or Double's ToString() method, and that the exact value is used (for example, .33333 is not equal to 1/3)
ToString()
Displays the fraction in a standard fraction format of [-]a b/c, [-]b/c, or [-]a, where the fractional component, if present, is always less than 1
ToImproperString()
Displays the fraction as either an improper fraction or an integer if there is no fractional component
Absolute()
Returns the absolute value
Defined Operators:
+
- Addition
-
- Subtraction
*
- Multiplication
/
- Division
-
- Negation
<
- Less Than
>
- Greater Than
=
- Equality
<>
- Inequality
<=
- Less Than Or Equal To
>=
- Greater Than Or Equal To
Defined Type Conversions (narrowing):
LDFraction to SByte
LDFraction to Byte
LDFraction to Short
LDFraction to UShort
LDFraction to Integer
LDFraction to UInteger
LDFraction to Long
LDFraction to ULong
LDFraction to Decimal
LDFraction to Single
LDFraction to Double
LDFraction to LargeDecimal
LDFraction to Fraction
Defined Type Conversions (widening):
Long to LDFraction
ULong to LDFraction
Decimal to LDFraction
Double to LDFraction
Fraction to LDFraction
LargeDecimal to Fraction
Source Code
LDFraction.vb:
Namespace NathanSokalski Public Class LDFraction Public Const FractionRegularExpression As String = "-?((\d+ )?\d+/\d+)|(\d+)" Public WholePart As New LargeDecimal(0) Public Numerator As New LargeDecimal(0) Private _sign As SByte = 1 Private _denominator As New LargeDecimal(1) Public Property Sign() As Integer Get Return Math.Sign(Me._sign) End Get Set(ByVal value As Integer) Me._sign = CSByte(Math.Sign(value)) End Set End Property Public Property Denominator() As LargeDecimal Get Return Me._denominator End Get Set(ByVal value As LargeDecimal) If value = 0 Then Throw New System.ArgumentException("You may not have a denominator of value 0") Else Me._denominator = value End Set End Property Public Sub New() Me.New(1, 0, 0, 1) End Sub Public Sub New(ByVal frac As String) Dim temp() As String If System.Text.RegularExpressions.Regex.IsMatch(frac, LDFraction.FractionRegularExpression) Then If frac.StartsWith("-") Then Me._sign = -1 Else Me._sign = 1 temp = frac.TrimStart("-"c).Split(" "c, "/"c) Select Case temp.Length Case 1 Me.WholePart = New LargeDecimal(temp(0)) Me.Numerator = 0 Me._denominator = 1 Case 2 Me.WholePart = 0 Me.Numerator = New LargeDecimal(temp(0)) Me._denominator = New LargeDecimal(temp(1)) Case 3 Me.WholePart = New LargeDecimal(temp(0)) Me.Numerator = New LargeDecimal(temp(1)) Me._denominator = New LargeDecimal(temp(2)) End Select If Me._denominator = 0 Then Throw New System.ArgumentException("You may not have a denominator of value 0", "frac") Me.Simplify() ElseIf IsNumeric(frac) Then If frac.StartsWith("-") Then Me._sign = -1 Else Me._sign = 1 If frac.Contains(".") Then Dim pieces() As String = frac.TrimStart("-"c).Split("."c) Me.WholePart = New LargeDecimal(pieces(0)) Me.Numerator = New LargeDecimal(pieces(1)) Me._denominator = New LargeDecimal(1, "1", pieces(1).Length) Else Me.WholePart = New LargeDecimal(frac.TrimStart("-"c)) Me.Numerator = 0 Me._denominator = 1 End If Me.Simplify() Else Throw New System.ArgumentException("You have entered an invalid fraction", "frac") End If End Sub Public Sub New(ByVal sign As Integer, ByVal intpart As LargeDecimal, ByVal num As LargeDecimal, ByVal denom As LargeDecimal) If denom = 0 Then Throw New System.ArgumentException("You may not have a denominator of value 0", "denom") If Not intpart.IsIntegral() OrElse intpart.Sign() = -1 Then Throw New System.ArgumentException("You must use a LargeDecimal with a positive integral value for the intpart parameter", "intpart") If Not num.IsIntegral() OrElse num.Sign() = -1 Then Throw New System.ArgumentException("The numerator must be a LargeDecimal with a positive integral value", "num") If Not denom.IsIntegral() OrElse denom.Sign() = -1 Then Throw New System.ArgumentException("The denominator must be a LargeDecimal with a positive integral value", "denom") Me._sign = CSByte(Math.Sign(sign)) Me.WholePart = intpart Me.Numerator = num Me._denominator = denom Me.Simplify() End Sub Public Sub New(ByVal intpart As LargeDecimal, ByVal num As LargeDecimal, ByVal denom As LargeDecimal) Me.New(1, intpart.Absolute(), num.Absolute(), denom.Absolute()) End Sub Public Sub New(ByVal num As LargeDecimal, ByVal denom As LargeDecimal) Me.New(1, 0, num.Absolute(), denom.Absolute()) 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 Private Sub Simplify() If Me.Numerator >= Me._denominator Then Me.WholePart += (Me.Numerator / Me._denominator).Truncate() Me.Numerator = Me.Numerator Mod Me._denominator End If If Me.Numerator = 0 Then Me._denominator = 1 If Me.WholePart = 0 AndAlso Me.Numerator = 0 Then Me._sign = 1 Dim i As New LargeDecimal(2) While i <= Me.Numerator If Me.Numerator Mod i = 0 AndAlso Me._denominator Mod i = 0 Then Me.Numerator = Me.Numerator / i Me._denominator = Me._denominator / i i = 2 Else : i += 1 End If End While End Sub Private Shared Function SubtractPositive(ByVal frac1 As LDFraction, ByVal frac2 As LDFraction) As LDFraction Dim temp1 As New LDFraction(frac1._sign, frac1.WholePart, frac1.Numerator, frac1._denominator) Dim temp2 As New LDFraction(frac2._sign, frac2.WholePart, frac2.Numerator, frac2._denominator) Dim tempsign As Integer Dim tempwhole, tempnum, tempdenom As LargeDecimal 'Make sure the denominators are equal If temp1._denominator = temp2._denominator Then tempdenom = temp1._denominator Else tempdenom = temp1._denominator * temp2._denominator temp1.Numerator = temp1.Numerator * temp2._denominator temp2.Numerator = temp2.Numerator * temp1._denominator temp1._denominator = tempdenom temp2._denominator = tempdenom End If 'Determine the sign, numerator, and denominator If temp2.WholePart > temp1.WholePart OrElse (temp2.WholePart = temp1.WholePart AndAlso temp2.Numerator > temp1.Numerator) Then tempsign = -1 If temp2.Numerator < temp1.Numerator Then temp2.Numerator = temp2.Numerator + temp2._denominator temp2.WholePart -= 1 End If tempnum = temp2.Numerator - temp1.Numerator tempwhole = temp2.WholePart - temp1.WholePart Else tempsign = 1 If temp1.Numerator < temp2.Numerator Then temp1.Numerator = temp1.Numerator + temp1._denominator temp1.WholePart -= 1 End If tempnum = temp1.Numerator - temp2.Numerator tempwhole = temp1.WholePart - temp2.WholePart End If Return New LDFraction(tempsign, tempwhole, tempnum, tempdenom) End Function Public Overrides Function ToString() As String Dim frac As New System.Text.StringBuilder() If Me._sign = -1 Then frac.Append("-") If Me.Numerator = 0 Then frac.Append(Me.WholePart) Else If Me.WholePart = 0 Then frac.AppendFormat("{0}/{1}", Me.Numerator, Me._denominator) Else frac.AppendFormat("{0} {1}/{2}", Me.WholePart, Me.Numerator, Me._denominator) End If End If Return frac.ToString() End Function Public Function ToImproperString() As String If Me.WholePart = 0 OrElse Me.Numerator = 0 Then Return Me.ToString() Else If Me._sign = -1 Then Return String.Format("-{0}/{1}", Me.WholePart * Me._denominator + Me.Numerator, Me._denominator) Else Return String.Format("{0}/{1}", Me.WholePart * Me._denominator + Me.Numerator, Me._denominator) End If End If End Function Public Function Absolute() As LDFraction Return Me * Me.Sign() End Function Public Shared Operator +(ByVal frac1 As LDFraction, ByVal frac2 As LDFraction) As LDFraction If frac1.Sign = frac2.Sign Then Dim tempsign As Integer = frac1.Sign Dim tempwhole As LargeDecimal = frac1.WholePart + frac2.WholePart If frac1._denominator = frac2._denominator Then Return New LDFraction(tempsign, tempwhole, frac1.Numerator + frac2.Numerator, frac1._denominator) Else Dim tempdenom As LargeDecimal = frac1._denominator * frac2._denominator Dim tempnum As LargeDecimal = frac1.Numerator * frac2._denominator + frac2.Numerator * frac1._denominator Return New LDFraction(tempsign, tempwhole, tempnum, tempdenom) End If ElseIf frac1.Sign = 1 Then Return frac1 - (-frac2) Else Return frac2 - (-frac1) End If End Operator Public Shared Operator -(ByVal frac1 As LDFraction, ByVal frac2 As LDFraction) As LDFraction Dim temp1 As New LDFraction(frac1._sign, frac1.WholePart, frac1.Numerator, frac1._denominator) Dim temp2 As New LDFraction(frac2._sign, frac2.WholePart, frac2.Numerator, frac2._denominator) If temp1.Sign = -1 AndAlso temp2.Sign = -1 Then temp1.Sign = 1 temp2.Sign = 1 Return LDFraction.SubtractPositive(temp2, temp1) ElseIf temp1.Sign = -1 AndAlso temp2.Sign = 1 Then temp1.Sign = 1 temp2.Sign = 1 Return New LDFraction("-1") * (temp1 + temp2) ElseIf temp1.Sign = 1 AndAlso temp2.Sign = -1 Then temp2.Sign = 1 Return temp1 + temp2 Else Return LDFraction.SubtractPositive(temp1, temp2) End If End Operator Public Shared Operator *(ByVal frac1 As LDFraction, ByVal frac2 As LDFraction) As LDFraction Dim tempsign As Integer = frac1.Sign * frac2.Sign Dim improper1 As String = frac1.ToImproperString().TrimStart("-"c) Dim improper2 As String = frac2.ToImproperString().TrimStart("-"c) If Not improper1.Contains("/") Then improper1 &= "/1" If Not improper2.Contains("/") Then improper2 &= "/1" Dim temp1() As String = improper1.Split("/"c) Dim temp2() As String = improper2.Split("/"c) Return New LDFraction(tempsign, 0, New LargeDecimal(temp1(0)) * New LargeDecimal(temp2(0)), New LargeDecimal(temp1(1)) * New LargeDecimal(temp2(1))) End Operator Public Shared Operator /(ByVal frac1 As LDFraction, ByVal frac2 As LDFraction) As LDFraction If frac2.WholePart = 0 AndAlso frac2.Numerator = 0 Then Throw New System.DivideByZeroException("You may not divide a number by zero") Dim tempsign As Integer = frac1.Sign * frac2.Sign Dim improper1 As String = frac1.ToImproperString().TrimStart("-"c) Dim improper2 As String = frac2.ToImproperString().TrimStart("-"c) If Not improper1.Contains("/") Then improper1 &= "/1" If Not improper2.Contains("/") Then improper2 &= "/1" Dim temp1() As String = improper1.Split("/"c) Dim temp2() As String = improper2.Split("/"c) Return New LDFraction(tempsign, 0, New LargeDecimal(temp1(0)) * New LargeDecimal(temp2(1)), New LargeDecimal(temp1(1)) * New LargeDecimal(temp2(0))) End Operator Public Shared Operator -(ByVal frac As LDFraction) As LDFraction Return New LDFraction(-1 * frac.Sign, frac.WholePart, frac.Numerator, frac._denominator) End Operator Public Shared Operator <(ByVal frac1 As LDFraction, ByVal frac2 As LDFraction) As Boolean Return (frac1 - frac2).Sign = -1 End Operator Public Shared Operator >(ByVal frac1 As LDFraction, ByVal frac2 As LDFraction) As Boolean Return (frac2 - frac1).Sign = -1 End Operator Public Shared Operator =(ByVal frac1 As LDFraction, ByVal frac2 As LDFraction) As Boolean Return frac1.Sign = frac2.Sign AndAlso frac1.WholePart = frac2.WholePart AndAlso frac1.Numerator = frac2.Numerator AndAlso frac1._denominator = frac2._denominator End Operator Public Shared Operator <>(ByVal frac1 As LDFraction, ByVal frac2 As LDFraction) As Boolean Return frac1.Sign <> frac2.Sign OrElse frac1.WholePart <> frac2.WholePart OrElse frac1.Numerator <> frac2.Numerator OrElse frac1._denominator <> frac2._denominator End Operator Public Shared Operator <=(ByVal frac1 As LDFraction, ByVal frac2 As LDFraction) As Boolean Return frac1 < frac2 OrElse frac1 = frac2 End Operator Public Shared Operator >=(ByVal frac1 As LDFraction, ByVal frac2 As LDFraction) As Boolean Return frac1 > frac2 OrElse frac1 = frac2 End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As SByte Return CType(frac.Sign * frac.WholePart, SByte) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As Byte Return CType(frac.Sign * frac.WholePart, Byte) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As Short Return CType(frac.Sign * frac.WholePart, Short) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As UShort Return CType(frac.Sign * frac.WholePart, UShort) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As Integer Return CType(frac.Sign * frac.WholePart, Integer) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As UInteger Return CType(frac.Sign * frac.WholePart, UInteger) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As Long Return CType(frac.Sign * frac.WholePart, Long) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As ULong Return CType(frac.Sign * frac.WholePart, ULong) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As Decimal Return CType(frac.Sign * (frac.WholePart + (frac.Numerator / frac._denominator)), Decimal) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As Single Return CType(frac.Sign * (frac.WholePart + (frac.Numerator / frac._denominator)), Single) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As Double Return CType(frac.Sign * (frac.WholePart + (frac.Numerator / frac._denominator)), Double) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As LargeDecimal Return frac.Sign() * (frac.WholePart + (frac.Numerator / frac.Denominator)) End Operator Public Shared Narrowing Operator CType(ByVal frac As LDFraction) As Fraction Return New Fraction(frac.Sign(), CType(frac.WholePart, ULong), CType(frac.Numerator, ULong), CType(frac.Denominator, ULong)) End Operator Public Shared Widening Operator CType(ByVal value As Long) As LDFraction Return New LDFraction(Math.Sign(value), Math.Abs(value), 0, 1) End Operator Public Shared Widening Operator CType(ByVal value As ULong) As LDFraction Return New LDFraction(1, value, 0, 1) End Operator Public Shared Widening Operator CType(ByVal value As Decimal) As LDFraction Return New LDFraction(value.ToString()) End Operator Public Shared Widening Operator CType(ByVal value As Double) As LDFraction Return New LDFraction(value.ToString()) End Operator Public Shared Widening Operator CType(ByVal frac As Fraction) As LDFraction Return New LDFraction(frac.Sign(), frac.WholePart, frac.Numerator, frac.Denominator) 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
Even though I have defined the type conversions for them, I recommend using caution when converting to any of the standard numeric types since not all possible values of the LDFraction class can be stored in them.