|
-
Feb 25th, 2007, 05:29 PM
#1
[VB.NET] Decimal to Fraction Conversion
I recently needed a way to convert decimal values into their fraction counterparts. A search on Google turned up this little gem: http://www.geocities.com/oosterwal/c...onversion.html originally written for VBA/VB6. Naturally I'm working in .NET, so I converted the code to VB.NET.
VB Code:
Function dec2frac(ByVal dblDecimal As Double) As String
'
' Excel function to convert decimal values to integer fractions.
'
' Original Written by: Erik Oosterwal
' Converted to .NET by: Chris Anderson
' Started on: November 16, 2006
' Completed on: November 16, 2006
' Converted on: February 25, 2007
'
Dim intNumerator, intDenominator, intNegative As Integer
' Declare integer variables
Dim dblFraction, dblAccuracy As Double
' Declare floating point variables as double precision.
Dim txtDecimal As String
' need a string representation of the input value
' in order to determine the required accuracy.
' Find the accuracy needed for the output by checking the number of digits behind the decimal point
' of the input value.
'
' dblAccuracy = 1 / 10 ^ (Len(CStr(dblDecimal - Fix(dblDecimal))) - 2)
'
' While the formula above should work, there is a serious error in the way Excel handles
' decimal numbers and there's a huge rounding error issue. Subtracting the int() of
' 12.1 from 12.1 produces 0.0999999999 or something similar. Obviously that won't
' work for our desired accuracy of the magnitude of the fractional part of the number
' so a slower more cumbersome method has to be used...
dblAccuracy = 0.1 ' Set the initial Accuracy level.
txtDecimal = dblDecimal.ToString ' Get a string representation of the input number.
For i As Integer = 0 To (txtDecimal.Length - 1) ' Check each character to see if it's a decimal point...
If txtDecimal.Substring(i, 1) = "." Then ' if it is then we get the number of digits behind the decimal
dblAccuracy = 1 / 10 ^ (txtDecimal.Length - i) ' assign the new accuracy level, and
Exit For ' exit the for loop.
End If
Next
intNumerator = 0 ' Set the initial numerator value to 0.
intDenominator = 1 ' Set the initial denominator value to 1.
intNegative = 1 ' Set the negative value flag to positive.
If dblDecimal < 0 Then
intNegative = -1 ' If the desired decimal value is negative,
' then set the negative value flag to
' negative.
End If
dblFraction = 0 ' Set the fraction value to be 0/1.
Do While Math.Abs(dblFraction - dblDecimal) > dblAccuracy ' As long as we're still outside the
' desired accuracy, then...
If Math.Abs(dblFraction) > Math.Abs(dblDecimal) Then ' If our fraction is too big,
intDenominator += 1 ' increase the denominator
Else ' Otherwise
intNumerator += intNegative ' increase the numerator.
End If
dblFraction = intNumerator / intDenominator ' Set the new value of the fraction.
Loop
Return intNumerator.ToString & "/" & intDenominator.ToString ' Display the numerator and denominator
End Function
It's not the prettiest, and it could probably be written a little more streamlined, but it does work. If anyone has any suggestions for improvement, please do.
-tg
-
May 3rd, 2007, 05:46 PM
#2
Hyperactive Member
Re: [VB.NET] Decimal to Fraction Conversion
A better way to go is to use the Euclidean Algorithm to find the gcd.
After finding the gcd, the rest is simple.
Code:
''' <summary>
''' Return a fraction string from a double.
''' </summary>
''' <param name="d">The double to convert.</param>
''' <returns>The converted string.</returns>
''' <remarks>Code written by Troy Lundin on May 3, 2007</remarks>
Function GetFraction(ByVal d As Double) As String
' Get the initial denominator: 1 * (10 ^ decimal portion length)
Dim Denom As Int32 = CInt(1 * (10 ^ tb1.Text.Split("."c)(1).Length))
' Get the initial numerator: integer portion of the number
Dim Numer As Int32 = CInt(tb1.Text.Split("."c)(1))
' Use the Euclidean algorithm to find the gcd
Dim a As Int32 = Numer
Dim b As Int32 = Denom
Dim t As Int32 = 0 ' t is a value holder
' Euclidean algorithm
While b <> 0
t = b
b = a Mod b
a = t
End While
' Return our answer
Return CInt(d) & " " & (Numer / a) & "/" & (Denom / a)
End Function
Prefix has no suffix, but suffix has a prefix.
-
May 2nd, 2008, 11:47 AM
#3
New Member
Re: [VB.NET] Decimal to Fraction Conversion
I have tried the first VBA code and it crashes Excel with numbers such as 0.001589546. I suspect it hangs in the accuracy verification part. I also tried the Euclidean GCD approach but I am not sure if Excel VBA understands tb1.Text.Split. Can this be converted to VBA for Excel?
-
May 2nd, 2008, 11:50 AM
#4
Re: [VB.NET] Decimal to Fraction Conversion
Did you try the code I posted, or the code that was on the page I linked to? The code I posted was for .NET, NOT VBA..... the original code was a module function written in Excel, so I can only imagine that it woks, but to what degree, I don't know. I only needed to go 4 places, so didn't check beyond that as to its accuracy.
-tg
-
May 2nd, 2008, 01:59 PM
#5
New Member
Re: [VB.NET] Decimal to Fraction Conversion
 Originally Posted by techgnome
Did you try the code I posted, or the code that was on the page I linked to? The code I posted was for .NET, NOT VBA..... the original code was a module function written in Excel, so I can only imagine that it woks, but to what degree, I don't know. I only needed to go 4 places, so didn't check beyond that as to its accuracy.
-tg
Thanks, I used the initial code from:
http://www.geocities.com/oosterwal/c...onversion.html
that is hanging in Excel.
I may try to reduce the number of digits, but was also intrigued by the Euclidean approach. Unfortunately, my VB skills are less than basic and this was the only example in VBA that I could find.
-
May 2nd, 2008, 03:56 PM
#6
Re: [VB.NET] Decimal to Fraction Conversion
I think this should work (converted the Euclidean method above from .NET to VB6 (which should be close enough to VBA for you).
Code:
Function GetFraction(ByVal d As Double) As String
' Get the initial denominator: 1 * (10 ^ decimal portion length)
Dim Denom As Long = CLng(1 * (10 ^ (Len(tb1.text) - Instr(tb1.Text, ".") -1))
' Get the initial numerator: integer portion of the number
Dim Numer As Long = CLng(Mid(tb1.Text, instr(tb1.text, ".")))
' Use the Euclidean algorithm to find the gcd
Dim a As Long = Numer
Dim b As Long = Denom
Dim t As Long = 0 ' t is a value holder
' Euclidean algorithm
While b <> 0
t = b
b = a Mod b
a = t
End While
' Return our answer
GetFraction = CSTR(CLng(d)) & " " & (Numer / a) & "/" & (Denom / a)
End Function
I think that'll do it.
-tg
-
Apr 6th, 2009, 08:10 PM
#7
Re: [VB.NET] Decimal to Fraction Conversion
 Originally Posted by Troy Lundin
A better way to go is to use the Euclidean Algorithm to find the gcd.
After finding the gcd, the rest is simple.
Code:
''' <summary>
''' Return a fraction string from a double.
''' </summary>
''' <param name="d">The double to convert.</param>
''' <returns>The converted string.</returns>
''' <remarks>Code written by Troy Lundin on May 3, 2007</remarks>
Function GetFraction(ByVal d As Double) As String
' Get the initial denominator: 1 * (10 ^ decimal portion length)
Dim Denom As Int32 = CInt(1 * (10 ^ tb1.Text.Split("."c)(1).Length))
' Get the initial numerator: integer portion of the number
Dim Numer As Int32 = CInt(tb1.Text.Split("."c)(1))
' Use the Euclidean algorithm to find the gcd
Dim a As Int32 = Numer
Dim b As Int32 = Denom
Dim t As Int32 = 0 ' t is a value holder
' Euclidean algorithm
While b <> 0
t = b
b = a Mod b
a = t
End While
' Return our answer
Return CInt(d) & " " & (Numer / a) & "/" & (Denom / a)
End Function
This doesn't always work, when I use this:I get:Just thought I'd mention it
-
Nov 22nd, 2010, 04:48 PM
#8
New Member
Re: [VB.NET] Decimal to Fraction Conversion
This doesn't always work, when I use this:
GetFraction(1.5R)
I get:
2 1/2
Just thought I'd mention it
This happens because the whole part of the number is get by CInt function: CInt(d)
So, if fractional part >= 0.5 then number is rounded to next integer...
The corrected code is bellow. I also replaced "tb1.Text" with "d.ToString" (I think, is appropriate for a function).
Code:
Function GetFraction(ByVal d As Double) As String
' Get the initial denominator: 1 * (10 ^ decimal portion length)
Dim Denom As Int32 = CInt(1 * (10 ^ d.ToString.Split("."c)(1).Length))
' Get the initial numerator: integer portion of the number
Dim Numer As Int32 = CInt(d.ToString.Split("."c)(1))
' Use the Euclidean algorithm to find the gcd
Dim a As Int32 = Numer
Dim b As Int32 = Denom
Dim t As Int32 = 0 ' t is a value holder
' Euclidean algorithm
While b <> 0
t = b
b = a Mod b
a = t
End While
'Get whole part of the number
Dim Whole As String = d.ToString.Split("."c)(0)
' Return our answer
Return Whole & " " & (Numer / a) & "/" & (Denom / a)
End Function
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|