[Function] Decimal2Fraction and Fraction2Decimal

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

[Function] Decimal2Fraction and Fraction2Decimal

13 Dec 2013, 13:38

[Function] Decimal2Fraction
Version 1.9

This is a function to convert a decimal number into a fraction and return that as a string.

It has options to return an improper fraction or architectural fraction that is in feet and inches.

The architectural option includes zero inches as is the custom in the work that I do but that could be changed or other output formatting options could be added.

Also there is an option to round to the nearest fraction desired.

For example in my circumstances I general round my fractions to the nearest 16th of an inch.

Code: Select all

;{[Function] Decimal2Fraction
; Fanatic Guru
; 2013 12 21
; Version 1.9
;
; Function to Convert a Decimal Number to a Fraction String
;
;------------------------------------------------
;
; Method:
;   Decimal2Fraction(Decimal, Options)
;
;   Parameters:
;   1) {Decimal} 				A decimal number to be converted to a fraction string
;   2) {Options ~= {Number}}		Round to this fractional Percision ie. 32 would round to the closest 1/32nd
;      {Options ~= {D}{Number}}	Round fractional to a {Number} limit of digits ie. D5 limits fraction to 5 digits
;      {Options ~= "I"}			Return Improper Fraction
;      {Options ~= "AA"}			Return in Architectural format with feet and inches
;      {Options ~= "A"}			Return in Architectural format with inches only
;   		Optional
;
;
; Example:
;	MsgBox % Decimal2Fraction(1.2345)
;	MsgBox % Decimal2Fraction(1.2345,"I")
;	MsgBox % Decimal2Fraction(1.2345,"A")			; Convert Decminal Inches to Inches Fraction/Inches"
;	MsgBox % Decimal2Fraction(1.2345,"AI")			; Convert Decminal Inches to Fraction/Inches"
;	MsgBox % Decimal2Fraction(1.2345,"AA16") 		; Convert Decimal Feet to Feet'-Inches Fraction/16th Inches"
;	MsgBox % Decimal2Fraction(14.28571428571429,"D5")	; Convert with round to a limit of 5 digit fraction
;	MsgBox % Decimal2Fraction(.28571428571429,"AAD5")	; Convert Decimal Feet to Feet'-Inches Fraction/Inches" with round to a limit of 5 digit fraction

Decimal2Fraction(Decimal, Options := "" )
{
	FormatFloat := A_FormatFloat
	SetFormat, FloatFast, 0.15
	Whole := 0
	if (Options ~= "i)D")
		Digits := RegExReplace(Options,"\D*(\d*)\D*","$1"), (Digits > 15 ? Digits := 15 : )
	else
		Precision := RegExReplace(Options,"\D*(\d*)\D*","$1")
	if (Options ~= "i)AA")
		Feet := Floor(Decimal), Decimal -= Feet, Inches := Floor(Decimal * 12), Decimal := Decimal * 12 - Inches
	if !(Options ~= "i)I")
		Whole := Floor(Decimal), Decimal -= Whole
	RegExMatch(Decimal,"^(\d*)\.?(\d*?)0*$",Match), N := Match1 Match2
	D := 10 ** StrLen(Match2)
	if Precision
		N := Round(N / D * Precision), D := Precision
	Repeat_Digits:
	Original_N := N, Original_D := D 
	Repeat_Reduce:
	X := 0, Temp_D := D 
	while X != 1
		X := GCD(N,D), N := N / X, D := D / X
	if Digits
	{
		if (Temp_D = D and D > 1)
		{
			if Direction
				((N/ D < Decimal) ? N+= 1 : D += 1)
			else
				((N/ D > Decimal) ? N-= 1 : D -= 1)
			goto Repeat_Reduce
		}
		if !Direction
		{
			N_Minus := Floor(N), D_Minus := Floor(D), N := Original_N, D := Original_D, Direction := !Direction
			goto Repeat_Reduce
		}
		N_Plus := Floor(N), D_Plus := Floor(D)
		if (StrLen(D_Plus) <= Digits and StrLen(D_Minus) > Digits)
			N := N_Plus, D := D_Plus
		else if (StrLen(D_Minus) <= Digits and StrLen(D_Plus) > Digits)
			N := N_Minus, D := D_Minus
		else
			if (Abs(Decimal - (N_Plus / D_Plus)) < Abs(Decimal - (N_Minus / D_Minus)))
				N := N_Plus, D := D_Plus
			else
				N := N_Minus, D := D_Minus
		if (StrLen(D) > Digits)
		{
			Direction := 0
			goto Repeat_Digits
		}
	}
	if (D = 1 and !(Options ~= "i)Inches"))
	{
		if (Options ~= "i)AA")
		{
			Inches += N
			if (Inches = 12)
				Feet ++=, Inches := 0
		}
		else
			Whole += N
		N := 0
	}
	N := Floor(N)
	D := Floor(D)
	if (Options ~= "i)AA")
		Output := Feet "'-" Inches (N and D ? " " N "/" D:"")"""" 
	else
		Output := (Whole ? Whole " ":"") (N and D ? N "/" D:"")((Options ~= "i)A") ? """":"")
	SetFormat, FloatFast, %FormatFloat%
	return (Whole + N ? Trim(Output) : 0)
}

GCD(A, B) 
{
    while B 
	   B := Mod(A|0x0, A:=B)
    return A
}
;}
The script uses a version of the Euclidean algorithm to find the GCD which is surprisingly simple but very fast.



[Function] Fraction2Decimal
Version 1.6

This is a function to convert a fraction string to decimal. It tries to account for things like spaces and - ' " in the fraction. It can handle a robust combinations of feet, inches and decimals.

Like: 6 feet 9.5" and 1.5 of 3 inches

It expects something more normal like this though: 6'-8 3/8"

Code: Select all

;{[Function] Fraction2Decimal
; Fanatic Guru
; 2013 12 18
; Version 1.6
;
; Function to Fraction String to a Decimal Number
;   Tries to account for any phrasing of feet and inches 
;------------------------------------------------
;
; Method:
;   Fraction2Decimal(Fraction, Unit)
;
;   Parameters:
;   1) {Fraction} 		A string representing a fraction to be converted to a decimal number
;   2) {Unit} = true    Include feet or inch symbol in return
;      {Unit} = false   Do not include feet or inch symbol in return
;           Optional - Default to false
;
; Example:
; 	MsgBox % Fraction2Decimal("7/8")
; 	MsgBox % Fraction2Decimal("1 7/8")
; 	MsgBox % Fraction2Decimal("1-7/8""") ; "" required to escape a literal " for testing
; 	MsgBox % Fraction2Decimal("2'1-7/8""") ; "" required to escape a literal " for testing
; 	MsgBox % Fraction2Decimal("2'-1 7/8""") ; "" required to escape a literal " for testing
; 	MsgBox % Fraction2Decimal("2' 1"" 7/8") ; "" required to escape a literal " for testing
;

Fraction2Decimal(Fraction, Unit := false)
{
        FormatFloat := A_FormatFloat
	SetFormat, FloatFast, 0.15
        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
        Output := Trim(Fraction,"""'")
        if Output is number
        {
            SetFormat, FloatFast, %FormatFloat%
            return Output (Unit ? (Has_Feet ? "'":(Has_Inches ? """":"")) : "")
        }
        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 ? N / D : (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
        Output := (Has_Neg ? "-":"") (Output ~= "." ? RTrim(RTrim(Output,"0"),".") : Output) (Unit ? (Has_Feet ? "'":(Has_Inches ? """":"")) : "")
        SetFormat, FloatFast, %FormatFloat%
        return Output
}
;}

Highlighted to Fraction
Version 1.6

Here is a script that uses the two above functions to take what is highlighted and convert it to a fraction. It has 4 hotkeys that output different formats.

Win+F . . . . . . . . . . Decimal or Fraction to Proper Fraction
Win+Ctrl+F . . . . . . Decimal or Fraction to Improper Fraction
Win+Alt+F . . . . . . . Decimal or Fraction to Architectural with a Precision of 1/16th inch
Win+Ctrl+Alt+F . . . Decimal or Fraction to Custom Format based on GUI input

It can also be used to convert fractions to different format fractions. Through the GUI you can also convert highlighted fractions to decimal.

Code: Select all

;{Highlighted to Fraction
; Fanatic Guru
; 2013 12 18
; Version 1.6
;
; Use Clipboard to Convert Highlighted Number to Fraction Format
; Number can be a decimal or decimal with a ' or " for feet and inches
; Number can be a fraction, proper or improper
; Number can be a robust combination of ' feet foot ft " inch in / of etc. and decimals
;
;}

; INITIALIZATION - ENVIROMENT
;{-----------------------------------------------
;

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
#SingleInstance force  ; Ensures that only the last executed instance of script is running
;}

; INITIALIZATION - VARIABLES
;{-----------------------------------------------
;
Precision := 256
;}

; INITIALIZATION - GUI
;{-----------------------------------------------
;
Gui, Add, Text,, Highlighted to Convert
Gui, Add, Edit, ReadOnly W210 vGuiHigh, %GuiHigh%
Gui, Add, Text, yp+30, Fraction Precision
Gui, Add, Edit, W80 vPrecision, %Precision%
Gui, Add, Radio, xp+100 yp-8 vRound Checked, 1/N
Gui, Add, Radio, yp+15, N Digits
Gui, Add, Radio, Xm yp+30 Group vStyle Checked, Proper Fraction
Gui, Add, Radio,, Improper Fraction
Gui, Add, Radio,, Architectural Feet and Inches
Gui, Add, Radio,, Architectural Inches Only
Gui, Add, Radio, yp+24, Convert to Decimal
Gui, Add, Button, yp+24 X40 W120 H30 Default, Convert
GuiControl, Focus, Style
;}

; HOTKEYS
;{-----------------------------------------------
;
#f::	;{ <-- Decimal or Fraction to Proper Fraction
	Style := 1
	gosub Clip_Convert
return
;}

#^f::	;{ <-- Decimal or Fraction to Improper Fraction
	Style := 2
	gosub Clip_Convert
return
;}

#!f::	;{ <-- Decimal or Fraction to Architectural - Feet and Inches  
	Style := 3
	gosub Clip_Convert
return
;}

#^!f::	;{ <-- Decimal or Fraction to Custom Fraction Format
	Clipboard := ""
	Send ^c
	ClipWait,.5
	GuiControl,, GuiHigh, %Clipboard%
	Gui, Show, AutoSize, Highlighted to Fraction
return
;}
;}

; SUBROUTINES
;{-----------------------------------------------
;
Clip_Convert:
	Clipboard := ""
	Send ^c
	ClipWait,.5
Convert:
	D := Fraction2Decimal(Clipboard,1)
	Dec_Feet := true 
	Dec_Inch := false
	if InStr(D, """")
		Dec_Inch := true, Dec_Feet := false
	X := RTrim(D, "'""")
	if Style = 1
		Clipboard := Decimal2Fraction((Dec_Inch ? X/12 : X),(Precision ? (Round = 2 ? "D":"") Precision:""))
	if Style = 2
		Clipboard := Decimal2Fraction((Dec_Inch ? X/12 : X),"I"(Precision ? (Round = 2 ? "D":"") Precision:""))
	if Style = 3
		Clipboard := Decimal2Fraction((Dec_Inch ? X/12 : X),"AA"(Precision ? (Round = 2 ? "D":"") Precision:""))
	if Style = 4
		Clipboard := Decimal2Fraction((Dec_Feet ? X*12 : X),"A"(Precision ? (Round = 2 ? "D":"") Precision:""))
	if Style = 5
		Clipboard := D
	Send ^v
return
;}

; SUBROUTINES - GUI
;{-----------------------------------------------
;
GuiClose:
GuiEscape:
	Gui, Hide
return

ButtonConvert:
	Gui, Submit
	gosub Convert
return
;}

; FUNCTIONS
;{-----------------------------------------------
;
#Include [Function] Decimal2Fraction.ahk
#Include [Function] Fraction2Decimal.ahk
;}
FG
Last edited by FanaticGuru on 19 May 2015, 17:19, edited 20 times 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
Guest10
Posts: 578
Joined: 01 Oct 2013, 02:50

Re: [Function] Decimal2Fraction

13 Dec 2013, 14:13

INITIAL testing indicates debut of a very promising function... :ugeek:
User avatar
AlphaBravo
Posts: 586
Joined: 29 Sep 2013, 22:59

Re: [Function] Decimal2Fraction

13 Dec 2013, 16:06

this is really good, may suggest using the i) option if !(Options ~= "i)I") or maybe IfNotInString, Options, i and IfInString, Options, a as in not case sensitive
Guest10
Posts: 578
Joined: 01 Oct 2013, 02:50

Re: [Function] Decimal2Fraction

13 Dec 2013, 16:36

WHENCE in the original script IfNotInString, Options, i and IfInString, Options, a are implemented? :ugeek:
AlphaBravo wrote:this is really good, may suggest using the i) option if !(Options ~= "i)I") or maybe IfNotInString, Options, i and IfInString, Options, a as in not case sensitive
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: [Function] Decimal2Fraction

13 Dec 2013, 16:39

MsgBox % Decimal2Fraction(1.2345,16,"A") returns 1'-1/4", but it should be 1'-2 13/16". Or am I just missing something?
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: [Function] Decimal2Fraction

13 Dec 2013, 16:43

Guest10 wrote:WHENCE in the original script IfNotInString, Options, i and IfInString, Options, a are implemented?
They are not implemented, but Alpha Bravo is suggesting that they should be added to make the options case-insensitive.
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

13 Dec 2013, 17:09

I added a lot of stuff in the first post.

Now there is a Decimal2Fraction and Fraction2Decimal.
And an example script that actual uses the two which is what I designed the functions for in the first place.

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

13 Dec 2013, 17:21

kon wrote:MsgBox % Decimal2Fraction(1.2345,16,"A") returns 1'-1/4", but it should be 1'-2 13/16". Or am I just missing something?
You are right. The architectural is all screwed up.

I was so busy trying to get the decimal to fraction part working that I did not do the architectural correctly which is my main purpose for the script.

1.2345 inches is 1-1/4"
1.2345 feet is NOT 1'-1/4"

I need to fix this.

I have a need for both the ability to convert decimal inches to fraction inches and decimal feet to feet and inches.

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

13 Dec 2013, 17:40

Edit:
*** Updated below at http://ahkscript.org/boards/viewtopic.p ... 7610#p7610 ***
I've been working on this (Not fully tested):

Code: Select all

MsgBox % DecFtToArch(1.2345,16)
return

DecFtToArch(DecFt,Precision) {
    Ft := Floor(DecFt), In := 12 * Mod(DecFt, 1), UpperLimit := 1 - (1 / (2 * Precision))
    Fraction := Mod(In, 1)
    In := Floor(In)
    if (Fraction >= UpperLimit) {
        In++
        if (In = 12)
            In := 0, Ft++
        Fraction := ""
    }
    else if (Fraction < 0.5 / Precision)
        Fraction := ""
    else {
        Step := 1 / Precision
        Loop, % Precision - 1 {
            if (Fraction >= UpperLimit - (A_Index * Step)) {
				n := Precision - A_Index
                break
            }
        }
        gcd := gcd(Precision, n)
        Fraction := " " n // gcd "/" Precision // gcd
    }
    return, (Ft ? Ft "'-" : "") In Fraction """"
}

gcd(a, b) {
    while (b != 0) {
        t := b
        b := Mod(a, b)
        a := t
    }
    return, a
}
Edit: Removed some unnecessary lines.
Last edited by kon on 18 Dec 2013, 16:08, edited 3 times in total.
User avatar
AlphaBravo
Posts: 586
Joined: 29 Sep 2013, 22:59

Re: [Function] Decimal2Fraction and Fraction2Decimal

13 Dec 2013, 18:04

Edit :
*** updated below ***
Last edited by AlphaBravo on 16 Dec 2013, 10:56, edited 1 time in total.
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: [Function] Decimal2Fraction and Fraction2Decimal

13 Dec 2013, 19:17

AlphaBravo wrote:testers needed
Very nice. I did find one thing.
Try: 0.999, 0.9165, 0.8332, 0.7498 ....
0.999 for example returns 0' 11" 1/1. It does this for values that are just under a whole inch (as above 12", 11", 10", 9"....).
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

13 Dec 2013, 19:29

Updated on First Page:

Change Log: [Function] Decimal2Fraction (version 1.3)

Made options case insensitive (thanks AlphaBravo, I knew about the i) flag but your eye for RegEx is always impressive)

Streamlined the options to look for a number in the options and use for precision which eliminated a parameter.

Optimized some of the code.

And most importantly fixed the Architectural option to correctly output feet and inches. If you use one "A" it assumes the input is inches and outputs inches. If you use two "AA" then it assumes the input is decimal feet and outputs feet and inches.

Fixed issue with faction of inches rounding up to an inch and then 12 inches need to convert to an additional foot.

Hopefully things work ok. Just wrote it so have not tested extensively.

I need to Update the Fraction2Decimal and Highlighted to Fraction script to properly deal with feet and inches but that will have to be another day. The changes should not be difficult.

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

13 Dec 2013, 20:24

Edit :
*** updated below ***
Last edited by AlphaBravo on 16 Dec 2013, 10:56, edited 1 time in total.
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

14 Dec 2013, 01:08

AlphaBravo wrote:I should have done it right the first time
There is still a problem with not rounding up.

1.9999 feet should round to 2 feet.

1.4999 feet should round to 1 foot 6 inches.

The technique of converting the input to basically an integer number of Bases (64 in this case) then at the end dividing by 2 both the top and the bottom of the fraction works (other than some rounding issues) as long as the Base is an exponential of 2 which traditionally measurements in feet and inches are, but this technique is not very flexible in converting decimals to fractions in general.

After I get the fluff of these functions working better, I might see if I can improve on the meat of the decimal to fraction which is basically the "while" section in my function. The meat works but it could be done faster I am sure with more thought.

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

14 Dec 2013, 11:58

that could easily be adjusted

Code: Select all

MsgBox % DecFtToArch(1.4999) "`n" DecFtToArch(1.499)

DecFtToArch(n, base := 64){
    B := n * 12 * base
    Ft := Floor(n)
    in := Floor(Mod(B/base - Ft*12 , 12))
    if ((Fr := Round(Mod(B - Ft*12*base - in*base  , base))) = base)
		in++, Fr := 0
	if (in = 12)
		Ft++, in := 0
    SetFormat, Float, 0.0
    while (Fr/2 = Floor(Fr/2))  && (Fr > 1)
        Fr := Fr/2 , base := base/2
    return Ft "' " In """"  (Fr ? " " Fr "/" Base : "")
}
Guest10
Posts: 578
Joined: 01 Oct 2013, 02:50

Re: [Function] Decimal2Fraction and Fraction2Decimal

14 Dec 2013, 12:24

IS this the final version to archive? :lol: :ugeek:
AlphaBravo wrote:that could easily be adjusted

Code: Select all

MsgBox % DecFtToArch(1.4999) "`n" DecFtToArch(1.499)

DecFtToArch(n, base := 64){
    B := n * 12 * base
    Ft := Floor(n)
    in := Floor(Mod(B/base - Ft*12 , 12))
    if ((Fr := Round(Mod(B - Ft*12*base - in*base  , base))) = base)
		in++, Fr := 0
	if (in = 12)
		Ft++, in := 0
    SetFormat, Float, 0.0
    while (Fr/2 = Floor(Fr/2))  && (Fr > 1)
        Fr := Fr/2 , base := base/2
    return Ft "' " In """"  (Fr ? " " Fr "/" Base : "")
}
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

14 Dec 2013, 12:31

AlphaBravo wrote:that could easily be adjusted
That seemed to have fixed the rounding problem and allowing the Base to be passed to the function is useful but it is problematic if the base is not an exponential of 2.

DecFtToArch(1.4,3) might be expected to return a precision to a third of an inch which it will not.

DecFtToArch(1.4,6) will return 1'1"3/6.

But as long as you are wanting a fraction with a denominator that is an exponential of 2 then the function seems to work ok.

My Decimal2Fraction function supports any precision in fractions while also providing options for Architectural output. If you want to round fractions to the nearest 57th it will do that.

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

14 Dec 2013, 19:31

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
}
Last edited by AlphaBravo on 15 Dec 2013, 19:19, edited 1 time in total.
User avatar
FanaticGuru
Posts: 1908
Joined: 30 Sep 2013, 22:25

Re: [Function] Decimal2Fraction and Fraction2Decimal

15 Dec 2013, 02:02

AlphaBravo wrote:I tried your function and it struggled for few of my trials, here is my own attempt
Yes, my function can be slow for fractions that involve large numbers. A fraction that results in something like 1234567/7654321 will take a long time to solve. What ever the resulting fraction is the function basically requires going through a loop the number of times equal to the top and bottom added together. It is mathematically sound but processor inefficient. Especially when you are putting in a decimal feet that gets converted to feet and inches with could resolve into a fraction of inches with very large nominators or dominators. That is a difficult problem to solve for any decimal to fraction algorithm that solves exactly without rounding.

If you are converting a 15 digit floating decimal that results in a 15 digit prime nominator and 15 digit prime dominator then I imagine the algorithm to solve it quickly is going to be quite complex.

Now if you round to something reasonable that is not a problem but I would not suggest very long decimals without passing some type of precision number to my function.

Decimal2Fraction(Sqrt(7))
That is going to result in a huge fraction that is going to be processor intensive to solve. Sqrt(7) is going to convert to the decimal length limit probably something like 2.645751311064591. That is going to result in a very long fraction to solve without rounding.

If you are converting literal decimals of less than about 5 digits you are fine. If are passing longer decimals or something that could result in longer decimals you need to provide some type of rounding.

I was thinking about trying to improve on the algorithm along the lines of your code but I was hoping to avoid using a table of primes as that limits the precision based on the largest prime in the table. You are basically rounding based on how large a prime you go up to in your table.

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.

For example I notices you put 4'3"4/5 but in the numbers I am dealing with it is general formatted 4'-3 4/5".

If I cannot get it working I might just post a list of examples and let you show me your RegEx wizardry.

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

15 Dec 2013, 13:20

Hi @Alppha Bravo. Once again, very nice function. If I may suggest two things regarding the "Factors" function.
This factors function should be more efficient, especially with large numbers, because it only has to loop through Sqrt(n) numbers instead of all n numbers.

Code: Select all

Factors(n,Str:=""){
    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 (Str)
		for key, val in factors
			res.=(res?",":"") key
    return Str?res:factors
}
I like to always initially store the numbers in an array, and then if I need a string returned, I just use a for-loop to get the string. The reason being that for perfect squares, the Sqrt would be added twice to the string. IE: for 9, 3 would be in the string twice, for 25, 5 would be in the string twice. Using an array makes sure that only one of each factor is present.
I also usually don't include 1 or n in the list of factors, but I just added n manually (factors[n] := n) because your original function had n included in the returned value also.

Another way, and I think the fastest way, you could reduce the fraction is by using the gcd(a, b) function that I used in my DecFtToArch(DecFt,Precision) function above. Just replace your for each, f in Factors(numerator) for-loop with:

Code: Select all

...
    gcd := gcd(denominator, numerator)
    fr := numerator//gcd "/" denominator//gcd
...
gcd(a, b) uses the Euclidean algorithm to find the greatest common divisor.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 208 guests