I am attempting to take decimal numbers from fields (i.e., 6.09375), convert them to a mixed fraction (i.e., 6 6/64), reduce the mixed fraction (i.e., 6 3/32), and then display the reduced fraction as text in a form. Is there a function that will convert a decimal to a mixed, reduced fraction? If not, do you have any suggestions on how to accomplish this task?
local a,b
a = 21.46876
b = ?(int(a)>0,int(a),0)
a = int(round((a-b)/0.015625,1.0))
b = str(b) +" "+ ?(a mod 32 = 0,str(a/32)+"/2",?(a mod 16 = 0,str(a/16)+"/4",
?(a mod 8 = 0,str(a/8)+"/8",?(a mod 4 = 0,str(a/4)+"/16",
?(a mod 2 = 0,str(a/2)+"/32",str(a)+"/64")))))
The variable, a, is your original number. The above code gives the result: 21 15/32
That code is inadequate in that it doesn’t cater for some boundary conditions and it’s unnecessarily complex. Here’s a version that should cater for all positive values (it will return an empty string if the input is zero):
local a,b
a = 21.46876
a = round(a,0.015625)
b = int(a)
a = (a-b)/0.015625
b = ?(b=0,"",str(b)+" ") + ?(a=0,"",
?(a mod 32 = 0,str(a/32)+"/2",?(a mod 16 = 0,str(a/16)+"/4",
?(a mod 8 = 0,str(a/8)+"/8",?(a mod 4 = 0,str(a/4)+"/16",
?(a mod 2 = 0,str(a/2)+"/32",str(a)+"/64"))))))
I have assumed that Paul’s data is empirical rather than abstract and that there will be no negative values. Paul, let me know if you do need to cater for negative numbers.
I was thinking about a different approach to this question, but before proceeding, would you explain what your formula does and why it works? Where did you get 0.015625?
Also, do you know how to generate the next prime number from any given prime? Or is there an array of primes available somewhere, up to some reasonably large value?
What you are searching for is called “The sieve of Eratosthenes”, and I have put together some pieces of code from Bill Bush, Jay Schille and David Thompson into one Panorama X database “Sieb des Eratosthenes”. Panorama X calculates e.g. the prime numbers from 2 to 150,000 in about 36 seconds.
The number 0.015625 is exactly 1/64 so this rounds the number to 64ths. If the fraction component is less than 1/128, it is set to zero; if it’s greater than 127/128, it’s set to zero and the mantissa is incremented by one, so an initial 13.993 would become 14.0. In the above case, the value of a becomes 13.484375, where 0.484375 is exactly 31/64.
a = round(a,0.015625)
The mantissa (integer component) is stored in b.
b = int(a)
The fraction component is divided by 0.015625 to calculate the number of 64ths.
a = (a-b)/0.015625
The mantissa is stored in b as a string and this is concatenated with the result of a set of cascading ?( functions which test for the number of 64ths in the variable, a, being a multiple of 0, 32, 16, 8, 4, 2 or 1 and then display the outcome as a fraction.
b = ?(b=0,"",str(b)+" ") + ?(a=0,"",
?(a mod 32 = 0,str(a/32)+"/2",?(a mod 16 = 0,str(a/16)+"/4",
?(a mod 8 = 0,str(a/8)+"/8",?(a mod 4 = 0,str(a/4)+"/16",
?(a mod 2 = 0,str(a/2)+"/32",str(a)+"/64"))))))
I posted a very similar but less elegant version as part of my Maths package on the Database Exchange some time back. The gold standard is still Dave Thompson’s Panorama 6.0 solution which is very much faster than the Panorama X one because it uses the chunkfilter statement which, sadly, could not be ported to Panorama X.
And your list of credits omits the lady who wrote the first pseudo-code - I forget her name, I think she was an Elizabeth.
And, finally, I just ran your code and it took less than 12 seconds to produce the primes up to 150,000.
Michael’s approach uses an approach that yields a fraction thatn is accurate to the nearest 1/64th. I came up with an approach to obtain a more accurate answer. In the example we started with, I think that 21.46876 is actually 21 11719/25000. Here is the code that I used:
setdialogtrigger ""
local lvn, lvRN, lva,lvb,lvx,lvpnum,
lvprimes,lvinput,lvI,lvest
lvest=""
lvinput=""
/* Get input value: lvinput, then break down into lvI and lvRN*/
alertsheetinput "Enter decimal number...",lvinput
/*This is a custom function on my computer that replaces Gettext*/
if info("dialogtrigger")="Cancel"
nsnotify "You cancelled!"
rtn
endif
lvI=array(lvinput,1,".")
lvRN=array(lvinput,2,".")
lvn=length(lvRN)
lva=val(lvRN)
lvb=10^lvn
/* The fraction is lva/lvb. Now we have to reduce the fraction by dividing by prime numbers*/
lvpnum=1
lvprimes=fileload("~/Desktop/primes.csv")
/*I used the database from Kurt to create an array of primes
from 2 to 500,000 and stored it in a file on my desktop. There are 41,538 primes in the array*/
lvx=val(array(lvprimes,lvpnum,","))
Tryx:
/*Test to see if lva and lvb are evenly divisible by lvx. If so, do it.
Repeat until it is not, then get the next prime and try it.*/
If lva mod lvx = 0 and lvb mod lvx = 0
lva=lva/lvx
lvb=lvb/lvx
goto Tryx
else
/*Get next prime number try it*/
lvpnum=lvpnum+1
lvx=val(array(lvprimes,lvpnum,","))
if lvpnum>41538
lvest=" (approximately)"
goto Done
endif
if lva≥lvx and lvb≥lvx
/*I think we could have a better test of when to stop testing primes.*/
goto Tryx
endif
endif
Done:
alertsheet "Final answer: "+lvinput+" is"+lvest+": "+lvI+" "+lva+"/"+lvb
nsnotify lvpnum + "primes used to test!"
I am not quite sure how to assess the accuracy when you get to longer decimal strings, but if you run out of primes before getting the exact answer, the result will indicate that the result is an approximation.
Just to pick some numbers, I tried 1 237/238, which equals 1.9957983193. If you enter that into my program, you get the answer 9957983193/10000000000. You run out of the primes and the program stops. Not very satisfactory. I think there are methods for finding the greatest common divisor of two numbers, so I will investigate doing that to reduce the fraction.
Since the numerator isn’t divisible by either 2 or 5, that is the reduced fraction. The trouble is that 237/238 will be a repeating decimal. Those ten digits can only be an approximation.
This sort of thing can only make sense if you consider the intended use. For example, a tape measure graduated in feet and inches will have graduations every 16th or perhaps 32nd of an inch. If you have calculated a diagonal or something, and you want to convert your decimal answer to something that will be easy to measure with that tape, it makes sense to round to the nearest 64th and then reduce the fraction.
We don’t really know if that’s the intended use here.