Fraction 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 Fraction class allows you to store and perform basic mathematical functions on fractions.
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
Fraction
Fraction 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
Fraction()
Constructor that creates a fraction with a value of 0
Fraction(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
Fraction(sign As Integer,intpart As ULong,num As ULong,denom As ULong)
Fraction(intpart As ULong,num As ULong,denom As ULong)
Fraction(num As ULong,denom As ULong)
Constructors that create a new Fraction 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
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
Fraction(value As Decimal)
Fraction(value As Double)
Constructor that creates a Fraction 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):
Fraction to SByte
Fraction to Byte
Fraction to Short
Fraction to UShort
Fraction to Integer
Fraction to UInteger
Fraction to Long
Fraction to ULong
Fraction to Decimal
Fraction to Single
Fraction to Double
Fraction to LargeDecimal
LargeDecimal to Fraction
LDFraction to Fraction
Decimal to Fraction
Double to Fraction
Defined Type Conversions (widening):
Long to Fraction
ULong to Fraction
Fraction to LDFraction
Source Code
Fraction.vb:
Namespace NathanSokalski Public Class Fraction Public Const FractionRegularExpression As String = "-?((\d+ )?\d+/\d+)|(\d+)" Public WholePart As ULong = 0 Public Numerator As ULong = 0 Private _sign As SByte = 1 Private _denominator As ULong = 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 ULong Get Return Me._denominator End Get Set(ByVal value As ULong) 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, Fraction.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 = CULng(temp(0)) Me.Numerator = 0 Me._denominator = 1 Case 2 Me.WholePart = 0 Me.Numerator = CULng(temp(0)) Me._denominator = CULng(temp(1)) Case 3 Me.WholePart = CULng(temp(0)) Me.Numerator = CULng(temp(1)) Me._denominator = CULng(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 = CULng(pieces(0)) Me.Numerator = CULng(pieces(1)) Me._denominator = CULng(Math.Pow(10, pieces(1).Length)) Else Me.WholePart = CULng(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 ULong, ByVal num As ULong, ByVal denom As ULong) If denom = 0 Then Throw New System.ArgumentException("You may not have a denominator of value 0", "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 ULong, ByVal num As ULong, ByVal denom As ULong) Me.New(1, intpart, num, denom) End Sub Public Sub New(ByVal num As ULong, ByVal denom As ULong) Me.New(1, 0, num, denom) 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 += CULng(Math.Floor(Me.Numerator / Me._denominator)) 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 ULong = 2 While i <= Me.Numerator If Me.Numerator Mod i = 0 AndAlso Me._denominator Mod i = 0 Then Me.Numerator = CULng(Me.Numerator / i) Me._denominator = CULng(Me._denominator / i) i = 2 Else : i += 1UL End If End While End Sub Private Shared Function SubtractPositive(ByVal frac1 As Fraction, ByVal frac2 As Fraction) As Fraction Dim temp1 As New Fraction(frac1._sign, frac1.WholePart, frac1.Numerator, frac1._denominator) Dim temp2 As New Fraction(frac2._sign, frac2.WholePart, frac2.Numerator, frac2._denominator) Dim tempsign As Integer Dim tempwhole, tempnum, tempdenom As ULong '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 -= 1UL 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 -= 1UL End If tempnum = temp1.Numerator - temp2.Numerator tempwhole = temp1.WholePart - temp2.WholePart End If Return New Fraction(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 Fraction Return Me * Me.Sign() End Function Public Shared Operator +(ByVal frac1 As Fraction, ByVal frac2 As Fraction) As Fraction If frac1.Sign = frac2.Sign Then Dim tempsign As Integer = frac1.Sign Dim tempwhole As ULong = frac1.WholePart + frac2.WholePart If frac1._denominator = frac2._denominator Then Return New Fraction(tempsign, tempwhole, frac1.Numerator + frac2.Numerator, frac1._denominator) Else Dim tempdenom As ULong = frac1._denominator * frac2._denominator Dim tempnum As ULong = frac1.Numerator * frac2._denominator + frac2.Numerator * frac1._denominator Return New Fraction(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 Fraction, ByVal frac2 As Fraction) As Fraction Dim temp1 As New Fraction(frac1._sign, frac1.WholePart, frac1.Numerator, frac1._denominator) Dim temp2 As New Fraction(frac2._sign, frac2.WholePart, frac2.Numerator, frac2._denominator) If temp1.Sign = -1 AndAlso temp2.Sign = -1 Then temp1.Sign = 1 temp2.Sign = 1 Return Fraction.SubtractPositive(temp2, temp1) ElseIf temp1.Sign = -1 AndAlso temp2.Sign = 1 Then temp1.Sign = 1 temp2.Sign = 1 Return New Fraction("-1") * (temp1 + temp2) ElseIf temp1.Sign = 1 AndAlso temp2.Sign = -1 Then temp2.Sign = 1 Return temp1 + temp2 Else Return Fraction.SubtractPositive(temp1, temp2) End If End Operator Public Shared Operator *(ByVal frac1 As Fraction, ByVal frac2 As Fraction) As Fraction 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 Fraction(tempsign, 0, CULng(temp1(0)) * CULng(temp2(0)), CULng(temp1(1)) * CULng(temp2(1))) End Operator Public Shared Operator /(ByVal frac1 As Fraction, ByVal frac2 As Fraction) As Fraction 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 Fraction(tempsign, 0, CULng(temp1(0)) * CULng(temp2(1)), CULng(temp1(1)) * CULng(temp2(0))) End Operator Public Shared Operator -(ByVal frac As Fraction) As Fraction Return New Fraction(-1 * frac.Sign, frac.WholePart, frac.Numerator, frac._denominator) End Operator Public Shared Operator <(ByVal frac1 As Fraction, ByVal frac2 As Fraction) As Boolean Return (frac1 - frac2).Sign = -1 End Operator Public Shared Operator >(ByVal frac1 As Fraction, ByVal frac2 As Fraction) As Boolean Return (frac2 - frac1).Sign = -1 End Operator Public Shared Operator =(ByVal frac1 As Fraction, ByVal frac2 As Fraction) 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 Fraction, ByVal frac2 As Fraction) 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 Fraction, ByVal frac2 As Fraction) As Boolean Return frac1 < frac2 OrElse frac1 = frac2 End Operator Public Shared Operator >=(ByVal frac1 As Fraction, ByVal frac2 As Fraction) As Boolean Return frac1 > frac2 OrElse frac1 = frac2 End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As SByte Return CSByte(frac.Sign * frac.WholePart) End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As Byte Return CByte(frac.Sign * frac.WholePart) End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As Short Return CShort(frac.Sign * frac.WholePart) End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As UShort Return CUShort(frac.Sign * frac.WholePart) End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As Integer Return CInt(frac.Sign * frac.WholePart) End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As UInteger Return CUInt(frac.Sign * frac.WholePart) End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As Long Return CLng(frac.Sign * frac.WholePart) End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As ULong Return CULng(frac.Sign * frac.WholePart) End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As Decimal Return CDec(frac.Sign * (frac.WholePart + (frac.Numerator / frac._denominator))) End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As Single Return CSng(frac.Sign * (frac.WholePart + (frac.Numerator / frac._denominator))) End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As Double Return CDbl(frac.Sign * (frac.WholePart + (frac.Numerator / frac._denominator))) End Operator Public Shared Narrowing Operator CType(ByVal frac As Fraction) As LargeDecimal Return frac.Sign() * (frac.WholePart + (CType(frac.Numerator, LargeDecimal) / CType(frac.Denominator, LargeDecimal))) 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 Fraction Return New Fraction(frac.Sign(), CType(frac.WholePart, ULong), CType(frac.Numerator, ULong), CType(frac.Denominator, ULong)) End Operator Public Shared Narrowing Operator CType(ByVal value As Decimal) As Fraction Return New Fraction(value.ToString()) End Operator Public Shared Narrowing Operator CType(ByVal value As Double) As Fraction Return New Fraction(value.ToString()) End Operator Public Shared Widening Operator CType(ByVal value As Long) As Fraction Return New Fraction(value.ToString()) End Operator Public Shared Widening Operator CType(ByVal value As ULong) As Fraction Return New Fraction(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 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 Fraction class can be stored in them.