[Function] Decimal2Fraction and Fraction2Decimal

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
AlphaBravo
Posts: 586
Joined: 29 Sep 2013, 22:59

Re: [Function] Decimal2Fraction and Fraction2Decimal

15 Dec 2013, 19:12

kon wrote:This factors function should be more efficient
@kon, this is beautiful, well thought out and very efficient, thank you so much for posting it.
I'll update my post above.

@FanaticGuru, your understanding of the task in hand is impeccable.
User avatar
AlphaBravo
Posts: 586
Joined: 29 Sep 2013, 22:59

Re: [Function] Decimal2Fraction and Fraction2Decimal

16 Dec 2013, 00:44

FanaticGuru wrote:Right now I am having more trouble getting my Fraction2Decimal script working correctly which sounds stupid as it is basically nothing but a practice in RegEx to handle every way I can think of writing: feet and/or inches with fractional feet or inches and weird ways I have seen stuff.
this code should take care of every possible way as it looks for the numbers and not the separators

Code: Select all

data =
(
0' 8-31/32"
2'
3' 0"
44' 11 31/32
4' 10" 31/32
4' 9 31/32"
0' 8-31/32"
11" 31/32
31/32
)
loop, parse, data, `n
	MsgBox % fr2dec(A_LoopField)
return

fr2dec(Fr) {
	while pos := RegExMatch(Fr, "\d+", m, pos?pos+StrLen(m):1)
		Nos := m . (A_Index=1?"":"`n") Nos
	No := StrSplit(Nos, "`n")
	IfInString, Fr, /
		denominator := No[1] , numerator := No[2]	,inches := No[3]?No[3]:0	, feet := No[4]?No[4]:0
	else if No[2]
		denominator := 1	 , numerator := 0		,inches := No[1]?No[1]:0	, feet := No[2]
	else
		denominator := 1	 , numerator := 0		,inches := 0				, feet := No[1]
	;~ ToolTip % feet "' " inches """ " numerator "/" denominator
	return feet + (inches + numerator/denominator) / 12
}
Guest10
Posts: 578
Joined: 01 Oct 2013, 02:50

Re: [Function] Decimal2Fraction and Fraction2Decimal

16 Dec 2013, 09:08

when i ran this it said: QPX(1) ... Error: Call to nonexistant function.!
AlphaBravo wrote:I tried your function and it struggled for few of my trials, here is my own attempt

Code: Select all

numbers := [14.285714, 1.2142857, 100/7]
QPX(1)
for each, n in numbers
	res1 .= Dec2Fr(n) "`n"
t1 := "`nt= " QPX(0) " seconds"
MsgBox % res1 t1 
return

Dec2Fr(n){
	PrimNo := [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]
	neg := n && n < 0 ? "-" : ""
	n := Abs(n)
	whole := Floor(n) ? Floor(n)" " : ""
	if (n = whole) || !n
		return neg floor(n)
	dec := n - floor(n)
	while (dec > floor(dec))
		dec*=10, dec := RTrim(RTrim(dec,0),.)
	exp := StrLen(dec)
	
	numerator:=floor(dec) 
	denominator := 10**exp
	fr := floor(numerator) "/" floor(denominator)
	
	for each, v in PrimNo
		if (Abs(round(n*v) - n*v) < 0.0001)		; close enough
			return neg whole floor(Mod(n*v,v)) "/" v

	for each, f in Factors(numerator)
		if !Mod(numerator,f) && !Mod(denominator,f)
			fr := floor(numerator/f) "/" floor(denominator/f)

	return  neg whole fr
}

Factors(n,Delim:=""){  ; http://ahkscript.org/boards/viewtopic.php?f=6&t=994#p7404
	factors := {}, factors[n] := n, d := 1, s := Floor(Sqrt(n))
	while (++d<=s)
		if !Mod(n,d)
			factors[d] := d, factors[n // d] := n // d
	if Delim
		for key, val in factors
			res.=(res?Delim:"") key
	return Delim?res:factors
}
User avatar
AlphaBravo
Posts: 586
Joined: 29 Sep 2013, 22:59

Re: [Function] Decimal2Fraction and Fraction2Decimal

16 Dec 2013, 10:39

Guest10 wrote:when i ran this it said: QPX(1) ... Error: Call to nonexistant function.!
QPX is not really part of the code, it's a performance query.
User avatar
joedf
Posts: 9000
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: [Function] Decimal2Fraction and Fraction2Decimal

16 Dec 2013, 11:02

this is cool! sorta reminds me of the fraction function on my trusty old Texas Instrument calculator TI-84+
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

16 Dec 2013, 14:52

AlphaBravo wrote:this code should take care of every possible way as it looks for the numbers and not the separators
That is exactly what I thought of when I was laying in bed last night. I don't need to figure out complex RegEx. The input is basically 1 to 4 numbers separated by "stuff". If I look at it that way I should be able to make it very versatile.

May sound strange but I am not going to look at your code and do what I have in my head and then compare.

I like coding like some people like doing crossword puzzles. I wasted so much time working on a complex RegEx, I want to see what I come up with on this one before I flip to the back and look at the answer.

On a side note, I have wished so many times there was a really nice RegEx editor that followed all the AutoHotkey specifics. I am getting better and better with RegEx but it is still trickier than it should be with a lot of trail and error.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
AlphaBravo
Posts: 586
Joined: 29 Sep 2013, 22:59

Re: [Function] Decimal2Fraction and Fraction2Decimal

16 Dec 2013, 16:30

FanaticGuru wrote:May sound strange but I am not going to look at your code and do what I have in my head and then compare.
you're just a Fanatic geek like the rest of us here! :ugeek:
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

16 Dec 2013, 18:45

Ok, I come up with what appears to be a very versatile Fraction to Decimal that allows for feet and inches.

Code: Select all

Fractions := [
(Join,
"4/8"
"1 4/8"
"-2'6"" 24/8"
"6'8"""
"5 ft 9 in"
"1ft6in"
"7 feet 8 / 10 inches"
"9 1 /4 feet"
"9 1 /4 inches"
"9 - 3 feet/inches"
"9 feet and 3 1/4 inches"
"4 and 8 out of 10 feet"
)]

for index, Fraction in Fractions
    MsgBox % Fraction2Decimal(Fraction)

Esc::ExitApp

Fraction2Decimal(Fraction)
{
        if Fraction is number
            return Fraction
        Num := {}
        N := 0
        D := 1
        if RegExMatch(Fraction, "^\s*-")
            Has_Neg := true
        if RegExMatch(Fraction, "i)feet|foot|ft|'")
            Has_Feet := true
        if RegExMatch(Fraction, "i)inch|in|""")
            Has_Inches := true
        if RegExMatch(Fraction, "i)/|of|div")
            Has_Fraction := true
        RegExMatch(Fraction,"^\D*(\d*)\D*(\d*)\D*(\d*)\D*(\d*)",Match)
        Loop 4
            if Match%A_Index%
                Num.Insert(Match%A_Index%)
        if Has_Fraction
        {
            N := Num[Num.MaxIndex()-1]
            D := Num[Num.MaxIndex()]
        }
        Output := (Num.MaxIndex() = 2 ? 0 : Num[1]) + N / D
        if (Has_Feet &  Has_Inches)
            if (Num.MaxIndex() = 2)
                Output := Num[1] + Num[2] /12
            else
                Output := Num[1] + ((Num.MaxIndex() = 3 ? 0:Num[2]) + N / D) / 12
        return (Has_Neg ? "-":"") Output
}
;}
With initial testing it seems to be able to handle everything I can think to throw at it and it is easy to add stuff that might be used to signify that a string contains feet, inches, and/or a fraction.

Now to look at implementing the best way I can find, not just that I can think of for reducing a fraction.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

17 Dec 2013, 04:27

Updated First Page

Change Log: Decimal2Fraction (Version 1.5)
After a quick reading of a Wiki I discovered that finding a Greatest Common Divisor is ridiculously simple to code. My preferred flavor below.

Code: Select all

GCD(A, B)
{
	if !B
		return A
	else
		return GCD(B, Mod(A,B))
}
So updated the script to use this GCD function which made the meat of the script basically just this:

Code: Select all

	while X != 1
	{
		X := GCD(Numerator,Denominator)
		Numerator := Numerator / X
		Denominator := Denominator / X
	}
Removed any default rounding. This simplified code will handle fractions up to the AHK limit of 15 digits. Although you will have to use SetFormat to see more than 6 digits.


Change Log: Fraction2Decimal (Version 1.4)
Added a Unit parameter to have it return the units if set to true. ie feet and inches symbol


Change Log: Highlighted to Fraction (Version 1.3)
Polished it up some and allowed very high Precision to be used.


It was fun to make my first stab at Fraction to Decimal code before looking it up. I would never have thought the GCD code would be so simple.

Most of the scripts now are just to handle dealing with feet and inches the way I want.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: [Function] Decimal2Fraction and Fraction2Decimal

17 Dec 2013, 11:32

I've done some speed tests on the GCD function in the past and found that the recursive functions are about 5x slower than using a while-loop like the function I posted above. (But the one-liner by Laszlo has a very high coolness factor.) Here they are with some speed tests:

Code: Select all

QPX( True )
Loop, 100000 {
	Random, Var1, 1, 100000
	Random, Var2, 1, 100000
	GCD1(Var1, Var2)
}
MsgBox, % "GCD1`n" QPX( False )

QPX( True )
Loop, 100000 {
	Random, Var1, 1, 100000
	Random, Var2, 1, 100000
	GCD2(Var1, Var2)
}
MsgBox, % "GCD2`n" QPX( False )

QPX( True )
Loop, 100000 {
	Random, Var1, 1, 100000
	Random, Var2, 1, 100000
	GCD3(Var1, Var2)
}
MsgBox, % "GCD3`n" QPX( False )

GCD1(A, B)
{
    if !B
        return A
    else
        return GCD1(B, Mod(A,B))
}

;http://www.autohotkey.com/board/topic/24841-least-common-multiple-and-greatest-common-factor/#entry161113
;by Laszlo
GCD2(a,b) {      ; Euclidean GCD
   Return b=0 ? Abs(a) : GCD2(b, mod(a,b))
}

;https://en.wikipedia.org/wiki/Euclidean_algorithm#Implementations
GCD3(a, b) {
    while (b != 0) {
        t := b
        b := Mod(a, b)
        a := t
    }
    return, a
}

QPX( N=0 ) { ; Wrapper for QueryPerformanceCounter()by SKAN | CD: 06/Dec/2009
	Static F,A,Q,P,X ; www.autohotkey.com/forum/viewtopic.php?t=52083 | LM: 10/Dec/2009
	If	( N && !P )
		Return	DllCall("QueryPerformanceFrequency",Int64P,F) + (X:=A:=0) + DllCall("QueryPerformanceCounter",Int64P,P)
	DllCall("QueryPerformanceCounter",Int64P,Q), A:=A+Q-P, P:=Q, X:=X+1
	Return	( N && X=N ) ? (X:=X-1)<<64 : ( N=0 && (R:=A/X/F) ) ? ( R + (A:=P:=X:=0) ) : 1
}
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

17 Dec 2013, 13:49

kon wrote:I've done some speed tests on the GCD function in the past and found that the recursive functions are about 5x slower than using a while-loop like the function I posted above. (But the one-liner by Laszlo has a very high coolness factor.) Here they are with some speed tests:
If we are shooting for best speed then I need to put my twist on it.

Code: Select all

GCD(A, B) 
{
    while B 
        T := B, B := Mod(A, B), A := T
    return A
}
Seems to provide consistently faster times than the fastest of the others.

Code: Select all

QPX( True )
Loop, 1000000 {
    Random, Var1, 1, 100000
    Random, Var2, 1, 100000
    GCD1(Var1, Var2)
}
MsgBox, % "GCD1`n" QPX( False )

QPX( True )
Loop, 1000000 {
    Random, Var1, 1, 100000
    Random, Var2, 1, 100000
    GCD2(Var1, Var2)
}
MsgBox, % "GCD2`n" QPX( False )

;https://en.wikipedia.org/wiki/Euclidean_algorithm#Implementations
GCD1(a, b) {
    while (b != 0) {
        t := b
        b := Mod(a, b)
        a := t
    }
    return, a
}

GCD2(a, b) 
{
    while b 
        t := b, b := Mod(a, b), a := t
    return, a
}

QPX( N=0 ) { ; Wrapper for QueryPerformanceCounter()by SKAN | CD: 06/Dec/2009
    Static F,A,Q,P,X ; www.autohotkey.com/forum/viewtopic.php?t=52083 | LM: 10/Dec/2009
    If  ( N && !P )
        Return  DllCall("QueryPerformanceFrequency",Int64P,F) + (X:=A:=0) + DllCall("QueryPerformanceCounter",Int64P,P)
    DllCall("QueryPerformanceCounter",Int64P,Q), A:=A+Q-P, P:=Q, X:=X+1
    Return  ( N && X=N ) ? (X:=X-1)<<64 : ( N=0 && (R:=A/X/F) ) ? ( R + (A:=P:=X:=0) ) : 1
}
I thought the recursive was cool but going to change it to my twist on the While in my script to get maximum performance.

Thanks for the speed comparison.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: [Function] Decimal2Fraction and Fraction2Decimal

17 Dec 2013, 14:05

Thanks. Your modified version will be my new go-to GCD function. :)
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

17 Dec 2013, 15:02

Updated First Page

Change Log: Decimal2Fraction (Version 1.6)
Changed GCD function to different flavor to improve performance.


Change Log: Fraction2Decimal (Version 1.5)
RTrim to remove trailing zeros and . when unneeded.


Change Log: Highlighted to Fraction (Version 1.5)
Use ClipWait instead of Sleep to improve reliability
Add Highlighted Number to Display in Gui Dialog
Restructured some things



FG
Last edited by FanaticGuru on 17 Dec 2013, 16:27, edited 1 time in total.
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
AlphaBravo
Posts: 586
Joined: 29 Sep 2013, 22:59

Re: [Function] Decimal2Fraction and Fraction2Decimal

17 Dec 2013, 15:43

FanaticGuru wrote:Removed any default rounding
I still think you should perform rounding, check the result of Fraction2Decimal(100/7)
with regards to Fraction2Decimal , you interpret 4/8 as 0.5 Ft, and I interpreted as 0.5 Inch :-)
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

17 Dec 2013, 17:23

AlphaBravo wrote:
FanaticGuru wrote:Removed any default rounding
I still think you should perform rounding, check the result of Fraction2Decimal(100/7)
with regards to Fraction2Decimal , you interpret 4/8 as 0.5 Ft, and I interpreted as 0.5 Inch :-)
I assume you are talking about doing:

Decimal2Fraction(100/7)

and not getting 100/7 back as an answer.

That is kind of a tough one because what you are actually passing to the function is:

14.285714285714286

Which then very literally gets converted to 14 285714285714286/1000000000000000 and then reduced cause for all the function knows the user really wanted the exact fraction of 14.285714285714286.

You can pass a precision to the function but unless you pass it a multiply of 7 as the precision you are never going to get 100/7 back out so that does not help.

Perhaps I can look at a null precision being unrounded and if the user passes a precision of zero then some type of smart rounding occurs. I will have to think about how to best make it round smart.

As for Fraction2Decimal, I tried to add more intelligence to the function.

Any time there is an indication of feet then it returns decimal feet, any time there is an indication of inches but no indication of feet then it returns decimal inches. It also keeps track of what it is returning and sends a unit symbol if the option is chosen.


4/8' should come back as .5'
6' 1/8" should come back as something like 6.010416666666667'

It can also handle things like:

6 - 3 feet/inches should come back 6.25'

I also added the ability to handle mixed decimals and fractions:

6 feet 9.5 inches

or even something as convoluted as:

6 feet 9.5" and 1.5 of 3 inches

It is in the same vein as your function and so much more flexible than when I was trying to unravel everything with a spaghetti pile of RegEx.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
Relayer
Posts: 160
Joined: 30 Sep 2013, 13:09
Location: Delaware, USA

Re: [Function] Decimal2Fraction and Fraction2Decimal

18 Dec 2013, 10:53

Decimal2Fraction(1.5,"AA") gives me 1'-7''

Relayer
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

18 Dec 2013, 15:34

Relayer wrote:Decimal2Fraction(1.5,"AA") gives me 1'-7''

Relayer
That was a silly mistake.

I had a check in there if the denominator of the faction reduced to a 1 I assumed the fraction rounded and ended up 1/1 and removed the fraction and added 1 to the inches.

But there is another much more common case where the fraction has a 1 denominator and that is when there is an even number of inches to start with then the fraction ends up 0/1 which also gets ignored but not before incrementing inches by 1.

Now I increment inches by the numerator of the fraction instead of a set 1.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: [Function] Decimal2Fraction and Fraction2Decimal

18 Dec 2013, 16:05

@FanaticGuru I hope you don't mind me putting this in your thread, but it seems like the best place.

This is an update of the DecFtToArch function I posted above. Also added is a DecInToArch function.

Code: Select all

;Convert decimal feet to Feet-Inches-Fraction
;	DecFt			Decimal feet
;	Precision		Round to the nearsest 1/Precision of an inch
DecFtToArch(DecFt, Precision:=16) {
    Ft := Floor(DecFt), In := 12 * Mod(DecFt, 1), UpperLimit := 1 - (HalfPrecision := 0.5 / Precision)
    Fraction := Mod(In, 1), In := Floor(In)
    if (Fraction >= UpperLimit) {
        In++, Fraction := ""
        if (In = 12)
            In := 0, Ft++
    }
    else if (Fraction < HalfPrecision)
        Fraction := ""
    else
        Fraction := GetFraction(Precision, Fraction, UpperLimit)
    return, (Ft ? Ft "'-" : "") In Fraction """"
}

;Convert decimal inches to Inches-Fraction
;	DecIn			Decimal inches
;	Precision		Round to the nearsest 1/Precision of an inch
DecInToArch(DecIn, Precision:=16) {
    In := Floor(DecIn), UpperLimit := 1 - (HalfPrecision := 0.5 / Precision), Fraction := Mod(DecIn, 1)
    if (Fraction >= UpperLimit)
        In++, Fraction := ""
    else if (Fraction < HalfPrecision)
        Fraction := ""
    else
        Fraction := GetFraction(Precision, Fraction, UpperLimit)
    return, (In ? In : "") Fraction """"
}

GetFraction(Precision, Fraction, UpperLimit) {
    Step := 1 / Precision
    Loop, % Precision - 1 {
        if (Fraction >= UpperLimit - (A_Index * Step)) {
            gcd := gcd(Precision, n := Precision - A_Index)
            return, "-" n // gcd "/" Precision // gcd
        }
    }
}

gcd(a, b) {
    while b
        t := b, b := Mod(a, b), a := t
    return, a
}
Example Usage:

Code: Select all

;Rounded to the nearest 16th of an inch by default
MsgBox, % DecFtToArch(1.23456)
MsgBox, % DecInToArch(19.2)

;Rounded to the nearest 1000000th of an inch
SetFormat, Float, 0.7
MsgBox, % DecFtToArch(123.4581236, 1000000)

;Rounded to the nearest 45678th of an inch
MsgBox, % DecInToArch(123.4581236, 45678)
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

18 Dec 2013, 20:01

Updated First Page

Change Log: Decimal2Fraction (Version 1.8)
Added SetFormat, FloatFast, 0.15 within function to improve rounding performance
SetFormat Settings are restored when function returns

Added a new option of "D" which will limit fractions to the number of digits sent as a number in the options
The "D" does not do a blunt rounding or truncating. It attempts to find fractions that are close to the decimal that will reduce to a simpler form to reduce the number of digits.

Decimal2Fraction(100/7,"D5")
will send 14.285714285714286 to the function and get back "14 2/7"

Decimal2Fraction(100/7,"16")
will send 14.285714285714286 to the function and get back "14 1/4" because that is the closest 1/16th

Decimal2Fraction(100/7)
will send 14.285714285714286 to the function and get back an exact conversion of 14 142857142857143/500000000000000



Change Log: Fraction2Decimal (Version 1.6)
Added SetFormat, FloatFast, 0.15 within function to allow the return of greater precision
SetFormat Settings are restored when function returns


Change Log: Highlighted to Fraction (Version 1.6)
Added a number to ClipWait even though the help says it defaults to .5 it seems to wait forever if you attempt to copy with nothing highlighted

Added options to use the new "D" maximum digit rounding to the Gui.


FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 261 guests