Write a program that converts a number entered in decimal format to a fraction that most closely approximates the entered number. The fraction must be in lowest terms and must equal the entered number when rounded back.
For example if 1.23456 is entered, the program should respond with the fraction 1879/1522.
Evaluate the fraction and you get 1.23455978... which equals the entered number when rounded back to 5 decimal places.
DIM N(25)
X = 1.23456
N(0) = INT(X)
A = X - N(0)
w1$ = LTRIM$(STR$(A))
DP = LEN(w1$) - 1
WHILE w1$ <> w2$
J = J + 1
b = 1 / A
N(J) = INT(b)
A = (b - N(J))
L = 0: K = 0
L = N(J) * N(J - 1) + 1
K = N(J)
FOR m = J - 2 TO 1 STEP -1
kk = L
L = L * N(m) + K
K = kk
NEXT m
x1 = INT(((K / L) * 10 ^ DP) + .5)
w2$ = "." + LTRIM$(STR$(x1))
WEND
PRINT X; " = "; L * N(0) + K; "/"; L
Your code works for the example I provided but not for some others:
.00123 = subscript out of range
3.1 = subscript out of range
5 = division by zero
12.3456789 = 581321 / 47087
The answers I get for these are, respectively: 1/813, 31/10, 5/1, 1355679/109810
Also, I did a poor job of defining the challenge. I neglected to say that the fraction should be as close as possible to the decimal without going over. Otherwise there would be 2 answers, one slightly above, and one slightly below. (This is known in the US as The Price Is Right Principle).
I am not brazen enough to try and change the rules again, so I have to accept your exact answer of 3858/3125 as the correct one. Here is my code which does minimize the numerator and denominator (I think).
INPUT "Enter Decimal"; d$
p = LEN(d$) - INSTR(d$, ".") + 1
d1 = VAL(d$)
wp = INT(d1)
dp = d1 - wp
DO
m = m + 1
x1 = m * dp
x2 = CLNG(x1)
dx = ABS(x1 - x2)
IF dx < min THEN min = dx: y = m: d2 = (CLNG(y * dp) + y * wp) / y
LOOP UNTIL ABS(d2 - d1) < 1 / 10 ^ p
PRINT d1; "="; CLNG(y * dp) + y * wp; "/"; y
END
I changed min and dx to Single. I recall problems could be solved by rounding to singles first. Integers can use Single with no rounding errors. Doubles is troubles!
'converts decimal to fractions by Lawgin
DEFDBL A-Z
DIM min AS SINGLE, dx AS SINGLE 'for all 9 divisor errors
min = 10
INPUT "Enter Decimal"; d$
p = LEN(d$) - INSTR(d$, ".") + 1
d1 = VAL(d$)
wp = INT(d1)
dp = d1 - wp
DO
m = m + 1
x1 = m * dp
x2 = CLNG(x1)
dx = ABS(x1 - x2)
IF dx < min THEN min = dx: y = m: d2 = (CLNG(y * dp) + y * wp) / y
LOOP UNTIL ABS(d2 - d1) < 1 / 10 ^ p
PRINT d1; "="; CLNG(y * dp) + y * wp; "/"; y
END
Ted
This message has been edited by burger2227 on Mar 12, 2009 12:27 PM This message has been edited by burger2227 on Mar 12, 2009 12:03 AM This message has been edited by burger2227 on Mar 11, 2009 10:58 PM This message has been edited by burger2227 on Mar 11, 2009 10:20 PM This message has been edited by burger2227 on Mar 11, 2009 10:05 PM
DIM dx AS SINGLE and it is fixed for that. But it may need more.
March 12 2009, 12:18 PM
I think that attempting fractions beyond 6 decimal places will not work anyhow. I could get a fraction for .000001, but not beyond that. The loop just keeps running.
So, taking that into account, I would recommend trying to use SINGLE variables instead of Doubles. Doubles are always a problem when you amplify the slight difference in each multiplication!
It is similar to the problems with exponents. You have to convert the result into a Single rather than trying LONGs using CLNG.
Ted
This message has been edited by burger2227 on Mar 12, 2009 1:03 PM This message has been edited by burger2227 on Mar 12, 2009 1:02 PM
The fractions are almost too accurate, but you can never get 1/3. The program needs to have some imagination to it! Like, if you enter .3 then you get 3/10, but if you enter .33333 you get 1/3. Some kind of rounding that still is mathmatically correct for the decimal places allowed.
1/3 is repetitive to many decimal point places beyond the progam's needs.
I dunno where to take this from here. Any ideas?
Ted
This message has been edited by burger2227 on Mar 13, 2009 1:47 AM This message has been edited by burger2227 on Mar 12, 2009 11:41 PM
Since 100/81 = 1.23456, the value of the fraction doesn't change.
There is a trivial solution to the challenge: Given a decimal w.d, we need only to find a number n such that n*w+n*d is a whole number. Then the fractional equivalent is (n*w+n*d)/n. Single precision math provides a built-in approximation of the fraction.
INPUT "Enter Decimal"; d$
d = VAL(d$)
wp = INT(d1)
dp = d - wp
DO
m = m + 1
x = m * wp + m * dp
IF x - INT(x) = 0 THEN EXIT DO
LOOP
PRINT d; "="; INT(x); "/"; m
END
Take 1.23456 as an example. The two nearest two digit fractions above and below are 79/64 and 100/81.
So using a graphical representation we are trying to construct a line that crosses a line of gradient 1.23456.
So start at a point (79,64) and form a line with gradient 100/81.
As the initial point is below the required gradient and 100/81 is larger than the required gradient then the constructed line will eventually cross the line of gradient 1.23456 and there is the solution.
so 1.23456 =(79+100*N)/(64+81*N) N=1,2,3,4,5....
Anyway here's another attempt at a program to do the hard work.
Remove the :END if you want all the solutions.
DEFLNG I-N
INPUT w$
x = VAL(w$)
dp = LEN(w$) - INSTR(w$, ".")
q$ = STRING$(dp + 2, "#")
FOR i = 1 TO 100000
FOR k = j TO 1 STEP -1
y = k / i
IF y - x < -.1 THEN 20
NEXT k
20 FOR j = k TO 100000
y = j / i
IF y - x > .1 THEN 10
y1 = INT(y * 10 ^ dp + .499) / (10 ^ dp)
IF y1 = x THEN
PRINT j; "/"; i; "=";
PRINT USING "###." + q$; y: END
END IF
NEXT j
10 NEXT i
I see that for the example 1.23456, the exact answer 3858/3125 is reached at about the 50th approximation. For many decimals, I like that you get the intuitive answer immediately:
.33=1/3
.667=2/3
.142857=1/7
3.14159=355/113
One minor bug---when there's only 1 decimal place, the results are erratic. Try .5 for example.