Convert Number to Fraction

I haven’t seen any other techniques for converting real number’s into fractions on this forum, so I just thought I’d share this version.
Along with ‘fractionising’ normal rational numbers, I have added a small example to illustrated how multiples of pi and any other known irrational numbers can also be found.

This has not been properly tested, so some bugs might be present.
I look forward to other versions that people have or any improvements on mine.

``````property tolerance : 10 -- Number of repeats
property minimumRemainder : 1.0E-7

property piSymbol : «data utxt03C0» as Unicode text
property squareRootSymbol : «data utxt221A» as Unicode text

-- Set up list of units
set unitList to {{unit:1, symbol:null}, {unit:pi, symbol:piSymbol}, {unit:(2 ^ 0.5), symbol:(squareRootSymbol & 2)}}

fractionise(2 ^ 0.5 * 0.45, unitList, tolerance, minimumRemainder)
--> "9âˆš2/20"

fractionise(3 * pi / 4, unitList, tolerance, minimumRemainder)
--> "3Ï€/4"

fractionise(12347 / 14512, unitList, 15, 1.0E-5) -- Custom tolerance levels
--> "12347/14512"

on fractionise(theNumber, unitList, tolerance, minimumRemainder)
repeat with thisUnit in unitList
tell fractionParts(theNumber / (thisUnit's unit), tolerance, minimumRemainder) to if it is not false then ¬
return my formatFraction(it, thisUnit's symbol)
end repeat
return false
end fractionise

on fractionParts(n, tolerance, minimumRemainder)
if n mod 1 is 0 then return {numerator:n div 1, denominator:1}

set i to n
set b to 1
repeat tolerance times
if i < 1 then
set remainder to i
else
set remainder to i - i div 1 -- Does the same as 'i mod 1', but more accurate
end if
if remainder < minimumRemainder then return {numerator:(n * b div 1), denominator:b div 1}
set i to 1 / remainder
set b to b * i
end repeat
return false
end fractionParts

on formatFraction(fraction, unitSymbol)
tell fraction
if unitSymbol is null then
set fracString to its numerator as Unicode text
else if its numerator is 1 then
set fracString to unitSymbol
else
set fracString to (its numerator as Unicode text) & unitSymbol
end if
if its denominator is not 1 then set fracString to fracString & "/" & its denominator
end tell

return fracString
end formatFraction
``````

Very nice, QD. Produces some interesting results occasionally:

fractionise((0.1111, unitList, tolerance, 1.0E-5) → 1111/10000

fractionise(0.11111, unitList, tolerance, 1.0E-5) → false

fractionise(0.111111, unitList, tolerance, 1.0E-5) → 1/9

It appears mod is slightly lossy, probably due to minor floating point errors.
Here is a comparison with a custom handler:

``````on modN(n, d)
return n - n div d
end modN

set n to 1 / 0.1111
return {correctResult:1 / 1111, ASMod:n mod 1, customMod:modN(n, 1)}
--> {correctResult:9.000900090009E-4, ASMod:9.00090008997267E-4, customMod:9.0009000900082E-4}
``````

As can be seen, the custom handler is more accurate, but not perfect.

I’ve now changed the line (in my original post):

``set remainder to i mod 1``

…into the following, which should hopefully improve accuracy:

```if i < 1 then set remainder to i else set remainder to i - i div 1 end if```
This now gives:

``````fractionise(0.1111, unitList, tolerance, 1.0E-10)
--> "1111/10000"

fractionise(0.11111, unitList, tolerance, 1.0E-7)
--> "11111/100000"

fractionise(0.111111, unitList, tolerance, 1.0E-5)
--> "1/9"
``````

I’m really impressed that it gets this right: fractionise(4.188790204786, unitList, tolerance, minimumRemainder) as 4Ï€/3

A very pleasing script, Qwerty. I attempted something similar a few years ago, but concluded that it couldn’t sensibly be done. This not only does it with a fair degree of success but returns niceties like pi and the square root of two. Nice one.