Number handling

I have a script calculating aspect ratios which finally is something like decimals to fraction and ‘optimizing fractions’.
Seems to work, but there is an oddity when converting decimals with repeating fraction digits

	set theNumber2 to (tmpVal - theNumber) as string -- string otherwise the "aVal mod 1" will fail 

If I don’t convert to string and then to real the following will fail
if aVal mod 1 = 0 then exit repeat

Bellow the script.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property cA : a reference to current application
property decDiv : "."
property notDecDiv : ","

set decDiv to (text 2 of ((1 / 2) as string))
if decDiv = "." then
	set notDecDiv to ","
else
	set notDecDiv to "."
end if

(* === work ? === *)
--set theNumber to "1.85"
--set repParts to 0

set theNumber to "1.833"
set repParts to 2
--set theNumber to "29.970029"
--set repParts to 6

set theNumber to my replaceString(theNumber, notDecDiv, decDiv)
my getFraction(theNumber, repParts)

(* === math === *)

on getFraction(theNumber, repParts)
	if repParts = 0 then
		# A, standard decimal
		set theNumber to theNumber as real
		set exp to 0
		repeat
			set bVal to (10 ^ exp)
			set aVal to (theNumber * bVal)
			if aVal mod 1 = 0 then exit repeat
			set exp to exp + 1
		end repeat
	else
		# B, repeating numbers
		set repExp to (10 ^ repParts)
		
		set decOff to (offset of decDiv in theNumber) + 1
		set decLen to length of (text decOff thru -1 of theNumber)
		set decExp to (10 ^ decLen)
		
		set repVal to (text -repParts thru -1 of theNumber)
		set repVal to (repVal / decExp)
		
		set tmpVal to ((theNumber * repExp) + repVal)
		set bVal to (repExp - 1) as integer
		
		set theNumber2 to (tmpVal - theNumber) as string -- string otherwise the "aVal mod 1" will fail 
		set theNumber2 to theNumber2 as real
		set exp to 0
		repeat
			set bVal2 to (10 ^ exp)
			set aVal to (theNumber2 * bVal2) as real
			if aVal mod 1 = 0 then exit repeat
			set exp to exp + 1
		end repeat
		set bVal to bVal * bVal2
		log {aVal, bVal}
	end if
	return my getRatio({aVal, bVal})
end getFraction


on getRatio(theNumbers)
	set {x, y} to theNumbers
	set res to ((x / y) * 1000) as integer
	set res to (res / 1000) as string
	
	repeat
		set i to gcd(x, y)
		if i = 1 then exit repeat
		set {x, y} to {x div i, y div i}
	end repeat
	set asp to {x, ":", y} as string
	
	--set {x, y} to theNumbers
	--set theNumbers to {x as integer, y as integer}
	return {asp, res, theNumbers}
	--return asp
end getRatio

on gcd(a, b) -- get greatest common divisor
	repeat while b > 0
		set {a, b} to {b, a mod b}
	end repeat
	return a
end gcd


(* === other === *)

on replaceString(sourceText, searchString, replacementString)
	set the sourceString to cA's NSString's stringWithString:sourceText
	try
		return (the sourceString's stringByReplacingOccurrencesOfString:searchString withString:replacementString) as string
	on error
		return (the sourceString)
	end try
end replaceString

The joy of floating-point values. The as string is doing some rounding.

Add a global called yy then insert some code so it reads like this:

		...
		set theNumber2 to (tmpVal - theNumber) as string -- string otherwise the "aVal mod 1" will fail 
		set theNumber2 to theNumber2 as real
		set nf to current application's NSNumberFormatter's alloc()'s init()
		nf's setFormat:"0.000000000000000"
		set yy to (nf's stringFromNumber:theNumber2) as text
		set exp to 0
		...

When you include as string you get:

"181.500000000000000"

When you don’t, you get:

"181.499999999999970"

Thanks Shane for conforming the “joy” part :wink:

It took me quite a while since I didn’t get

“181.500000000000000” <-> “181.499999999999970”

but (using 'log") some ‘1815.0’ mod 1 -> ‘1.0’