Author Topic: Convert Decimal to Fraction  (Read 10348 times)

0 Members and 1 Guest are viewing this topic.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Convert Decimal to Fraction
« on: April 22, 2019, 11:34:25 am »
I was looking over some of the programs in the DOS WORLD QBASIC.zip package:
https://www.qb64.org/forum/index.php?topic=1085.msg102795#msg102795

 and became interested in DEC_FRAC.BAS:
Code: QB64: [Select]
  1. '  DEC_FRAC.BAS - Fraction/Decimal conversion functions
  2. '                and sample program
  3.  
  4. '  by Antonio and Alfonso De Pasquale
  5. '  Copyright (C) 1993 DOS Resource Guide
  6. '  Published in Issue #10, July 1993, page 46
  7.  
  8. DECLARE FUNCTION DecToFrac$ (decimal)
  9. DECLARE FUNCTION FracToDec (fraction$)
  10.  
  11. MAIN:
  12. CLS: LOCATE 1, 25: PRINT "Fraction/Decimal Converter"
  13. LOCATE 2, 21: PRINT "by Antonio and Alfonso De Pasquale"
  14. LOCATE 3, 1: FOR x = 1 TO 79: PRINT "=";: NEXT x
  15. LOCATE 5, 1: PRINT "Please select one of the following choices:"
  16. LOCATE 7, 10: PRINT "[D]ecimal to Fraction"
  17. LOCATE 8, 10: PRINT "[F]raction to Decimal"
  18. LOCATE 9, 10: PRINT "[Q]uit Program"
  19.  
  20.     LOCATE 11, 1: PRINT SPACE$(79)
  21.     LOCATE 11, 1: INPUT "Please enter your choice (D/F/Q): ", choice$
  22.     choice$ = UCASE$(LEFT$(choice$, 1))
  23. LOOP UNTIL choice$ = "D" OR choice$ = "F" OR choice$ = "Q"
  24.  
  25. CONVERT:
  26. SELECT CASE choice$
  27.     CASE "Q"
  28.         CLS: END
  29.  
  30.     CASE "D"
  31.         LOCATE 13, 1: PRINT SPACE$(79)
  32.         LOCATE 13, 1: INPUT "Please enter a decimal value: ", decimal$
  33.         decimal = VAL(decimal$)
  34.         IF decimal = 0 OR INT(decimal) = decimal THEN GOTO CONVERT
  35.         fraction$ = DecToFrac$(decimal)
  36.         LOCATE 16, 1: PRINT "The Decimal  "; decimal; "  is equal to the fraction; "; fraction$; ""
  37.  
  38.     CASE "F"
  39.         LOCATE 13, 1: PRINT SPACE$(79)
  40.         LOCATE 13, 1: INPUT "Please enter a fractional value: ", fraction$
  41.         fl$ = fraction$: fl$ = fl$ + " ": fl = INSTR(1, fl$, "/")
  42.         IF VAL(fraction$) = 0 OR fl = 0 THEN GOTO CONVERT
  43.         IF (MID$(fl$, fl - 1, 1) = " ") OR (MID$(fl$, fl + 1, 1) = " ") THEN GOTO CONVERT
  44.         decimal = FracToDec(fraction$)
  45.         LOCATE 16, 1: PRINT "The fraction  "; fraction$; "  is equal to the decimal  "; decimal
  46.  
  47.  
  48. LOCATE 19, 1: PRINT "Press Enter to continue";
  49. GOTO MAIN
  50.  
  51. '*********************************************************
  52. '
  53. '         ACTUAL CONVERSION FUNCTIONS BEGIN HERE
  54. '
  55. '*********************************************************
  56.  
  57. FUNCTION DecToFrac$ (decimal)
  58.  
  59.     decimal$ = STR$(decimal)
  60.     index = INSTR(decimal$, ".")
  61.    
  62.     IF index = 1 THEN
  63.         decimal$ = "0" + decimal$
  64.         index = index + 1
  65.     END IF
  66.  
  67.     whole$ = LEFT$(decimal$, index - 1)
  68.     dec$ = MID$(decimal$, index, 10)
  69.  
  70.     IF VAL(whole$) = 0 THEN
  71.         whole$ = ""
  72.     END IF
  73.  
  74.     dec = VAL(dec$)
  75.     dec = INT(dec * 1000 + .5)
  76.  
  77.     num = dec
  78.     den = 1000
  79.  
  80.     FOR pass = 0 TO 3
  81.         FOR index = 10 TO 1 STEP -1
  82.             IF (num / index = INT(num / index)) AND (den / index) = INT(den / index) THEN
  83.                 num = (num / index)
  84.                 den = (den / index)
  85.             END IF
  86.         NEXT index
  87.     NEXT pass
  88.  
  89.     fraction$ = whole$ + STR$(num) + "/" + MID$(STR$(den), 2)
  90.     DecToFrac$ = fraction$
  91.  
  92.  
  93. FUNCTION FracToDec (fraction$)
  94.  
  95.     decimal = 0
  96.     dp = 0
  97.  
  98.     index = INSTR(fraction$, CHR$(32))
  99.  
  100.     IF index = 0 THEN
  101.         f$ = fraction$
  102.     ELSE
  103.         whole$ = LEFT$(fraction$, index)
  104.         f$ = MID$(fraction$, index + 1, 10)
  105.     END IF
  106.  
  107.     index = INSTR(f$, "/")
  108.     num = VAL(LEFT$(f$, index - 1))
  109.     den = VAL(MID$(f$, index + 1))
  110.  
  111.     dp = num / den
  112.     decimal = ABS(VAL(whole$)) + dp
  113.     IF LEFT$(fraction$, 1) = "-" THEN decimal = (-decimal)
  114.     IF LEFT$(fraction$, 1) = "-" AND decimal > 0 THEN decimal = (-decimal)
  115.     FracToDec = decimal
  116.  
  117.  

I tried a couple of 6 digit decimals .125125 and .333111 and saw that the method only seemed to handle 3 digits, ie answers back were for .125 and .333. The method used was a little curious too...

I thought I'd challenge folks to see if anyone could come up with better, elegant method, one that could at least come up with better answers for the tests mentioned.

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Convert Decimal to Fraction
« Reply #1 on: April 22, 2019, 03:15:36 pm »
My string math routine does a much better job of converting a fraction to a decimal. I don't have, nor do I want to make a way to convert a decimal to a fraction.

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Jack002

  • Forum Regular
  • Posts: 123
  • Boss, l wanna talk about arrays
    • View Profile
Re: Convert Decimal to Fraction
« Reply #2 on: April 22, 2019, 04:00:13 pm »
The idea of converting any decimal to a fraction is huge.
If you had to do it yourself, you'd be able to see X/10, X/5, X/3, X/9. Some know the pattern of 1/7 and might see that.
I saw a cool youtube vid where they claim any number no matter how long (like 15-20 digits mostly) they could tell you if it was prime or what factors it had. Some tricky little hacks to do it.
If I find that link, I'll post it here.

[edit]

Not the one I saw, but seems similar

« Last Edit: April 22, 2019, 04:14:51 pm by Jack002 »
QB64 is the best!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Convert Decimal to Fraction
« Reply #3 on: April 22, 2019, 05:30:14 pm »
Hint: You don't have to find all the factors of a number, though prime sieving is fun too! :)

27 lines of code, .333111 >>> 333111 / 1,000,000  .125125 >>> 1001 / 8000

Bonus .125125125 >>> 1001001 / 8,000,000

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Convert Decimal to Fraction
« Reply #4 on: April 23, 2019, 12:51:21 pm »
Code: QB64: [Select]
  1.     PRINT: INPUT "Enter decimal to convert to fraction, 0 quits "; dec$
  2.     IF dec$ = "0" THEN END ELSE PRINT convert2Fraction$(dec$)
  3.  
  4. FUNCTION convert2Fraction$ (decimal$)
  5.     dot%% = INSTR(decimal$, ".")
  6.     IF dot%% > 0 THEN
  7.         whole$ = MID$(decimal$, 1, dot%% - 1)
  8.         p%% = LEN(decimal$) - dot%%
  9.         n&& = VAL(MID$(decimal$, dot%% + 1))
  10.         d&& = 10 ^ p%%
  11.         g&& = gcd&&(n&&, d&&): sn&& = n&& / g&&: sd&& = d&& / g&&
  12.         convert2Fraction$ = whole$ + " " + _TRIM$(STR$(sn&&)) + "/" + _TRIM$(STR$(sd&&))
  13.     ELSE
  14.         convert2Fraction$ = decimal$
  15.     END IF
  16.  
  17. FUNCTION gcd&& (a&&, b&&)
  18.     'a and b will be changed unless make copies
  19.     c&& = a&&: d&& = b&&
  20.     WHILE c&& <> 0 AND d&& <> 0
  21.         IF c&& > d&& THEN c&& = c&& MOD d&& ELSE d&& = d&& MOD c&&
  22.     WEND
  23.     gcd&& = c&& + d&&
  24.  
  25.  

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Convert Decimal to Fraction
« Reply #5 on: April 23, 2019, 02:40:18 pm »
If you enter too many digits, it just returns 1/1. Large decimals like 1 / 97 cannot be properly evaluated. Because it can't round, it would just give a giant numerator and denominator, anyway. It works great for common fractions.

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Convert Decimal to Fraction
« Reply #6 on: April 23, 2019, 04:25:08 pm »
If you enter too many digits, it just returns 1/1. Large decimals like 1 / 97 cannot be properly evaluated. Because it can't round, it would just give a giant numerator and denominator, anyway. It works great for common fractions.

Pete

Works for up to 18 digits, a little better than 3 don't ya think?

Because it can't reduce (not round) it will give giant denominators. I only promised a fraction not convenience nor practicality. :D

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Convert Decimal to Fraction
« Reply #7 on: April 23, 2019, 10:10:48 pm »
"I never promised you a rose garden..."

Pete :D
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Jack002

  • Forum Regular
  • Posts: 123
  • Boss, l wanna talk about arrays
    • View Profile
Re: Convert Decimal to Fraction
« Reply #8 on: April 23, 2019, 11:44:12 pm »
If you enter too many digits, it just returns 1/1. Large decimals like 1 / 97 cannot be properly evaluated. Because it can't round, it would just give a giant numerator and denominator, anyway. It works great for common fractions.

Pete
Oh but if you ever get a chance, look at the recip of 97, its pretty cool. I did it in LISP once.
QB64 is the best!

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Convert Decimal to Fraction
« Reply #9 on: April 24, 2019, 12:20:48 am »
Works for up to 18 digits, a little better than 3 don't ya think?

Because it can't reduce (not round) it will give giant denominators. I only promised a fraction not convenience nor practicality. :D

It has to round to some degree to reduce properly. To try and get that for all possible decimal to fraction conversion would be a real... challenge.

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: Convert Decimal to Fraction
« Reply #10 on: April 24, 2019, 04:33:46 am »
Works for up to 18 digits, a little better than 3 don't ya think?

bplus, another brilliancy.  But what does WHILE 1 mean?  My brain doesn't like that statement at all.  Clearly it's not 0 and therefore NOT False and therefore the condition is True, but it just looks horrible (to me).  I think that I'd be happier with WHILE -1 (which is the BASIC convention for True), but even that I don't like.  I'd have to write: Condition%% = -1, WHILE Condition%%.  Good thing that I never got a job requiring programming.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Convert Decimal to Fraction
« Reply #11 on: April 24, 2019, 09:40:37 am »
Hi Qwerky,

How about: WHILE 1 = 1 ?

Actually with QB64 you can do infinite loops (or loops with escape or end coded inside) with DO... LOOP so:
Code: QB64: [Select]
  1.     PRINT: INPUT "Enter decimal to convert to fraction, 0 quits "; dec$
  2.     IF dec$ = "0" THEN END ELSE PRINT convert2Fraction$(dec$)
  3.  

might satisfy your QB senses. :)

Offline Jack002

  • Forum Regular
  • Posts: 123
  • Boss, l wanna talk about arrays
    • View Profile
Re: Convert Decimal to Fraction
« Reply #12 on: April 24, 2019, 09:47:38 am »
What about this?

For i = 1 to 2 step 0
    REM Now you're stuck here
next i

;)

QB64 is the best!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Convert Decimal to Fraction
« Reply #13 on: April 24, 2019, 09:54:57 am »
Oh but if you ever get a chance, look at the recip of 97, its pretty cool. I did it in LISP once.

It has to round to some degree to reduce properly. To try and get that for all possible decimal to fraction conversion would be a real... challenge.

Pete

Hi Jack002,

Yes, I know what you are talking about and I have some code floating about that calculates precisely the decimal and the repeating digits part of the decimal like we learned in school for 1/7 = .142857 with a bar over the digits that repeat forever.


Hi Pete,

Yes, I think it is possible to do any fraction BUT first you must express the decimal with a special notation to handle the repeating part. I like 1/3 = .R3 = .33333...   1/6 = .1R6 = .16666...

I will try to dig up the code I had that did that and then I will try to reverse it back from the special decimal notation eg, .1R6 back to 1/6. There will likely be a limit on digits again until/unless you convert the code to employ your string math.


Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Convert Decimal to Fraction
« Reply #14 on: April 24, 2019, 11:38:54 am »
OK I have the special decimal notation working (I think):
Code: QB64: [Select]
  1. _TITLE "n divided by d using R notation" '2019-04-24 R stands for Repeat section not Remainder
  2. ' from:"Decimal Expansion of Division without Dividing  by bplus 2017-12-03"
  3. ' dove tailing Adrians and my recent dividing programs
  4.  
  5. ' 2019-04-24
  6. ' I want to isolate the repeated section immdeiately not write the fraction out and then repeat the repeated section
  7. ' not 1/6 = .16R6 >>> but .1R6
  8.  
  9. 'hmm... look like have to cycle through twice to get the length of the repeat section
  10. ' but before returning backup to start of repeat section insert the R where it starts the first time
  11. ' and end it where " repeat " starts.
  12.  
  13.  
  14. DEFLNG A-Z
  15.     PRINT: PRINT "Enter 2 integers < 3200, numerator / denominator, 0's quit, don't forget / "
  16.     INPUT nd$
  17.     slash = INSTR(nd$, "/")
  18.     IF slash THEN
  19.         dvsr = VAL(MID$(nd$, slash + 1))
  20.         IF dvsr = 0 THEN PRINT "Divisor is 0, bye.": END
  21.         numerator = VAL(MID$(nd$, 1, slash - 1))
  22.         IF numerator = 0 THEN PRINT "Numerator is 0, bye.": END
  23.     ELSE
  24.         PRINT "No slash found, bye.": END
  25.     END IF
  26.     PRINT numerator; " / "; dvsr; " = "; divide$(numerator, dvsr)
  27.  
  28. FUNCTION divide$ (n, d)
  29.     'n = original product or numerator (preserve value of n)
  30.     'd = divisor  (also preserve value)
  31.     c = n 'copy of n to be reduced until <= d, c will be the remainder part of division
  32.     a = 0 'a is for answer or accumulate, the integer part of the division result
  33.  
  34.     'find lowest power of 10 such that: d * 10^p > n
  35.     p = 0 'power of 10
  36.     WHILE d * (10 ^ p) < n
  37.         p = p + 1
  38.     WEND
  39.     WHILE c >= d
  40.         IF c = d THEN a = a + 1: c = 0: EXIT WHILE
  41.         p = p - 1
  42.         IF p >= 0 THEN
  43.             m = 0
  44.             WHILE d * m * 10 ^ p < c
  45.                 m = m + 1
  46.             WEND
  47.             m = m - 1
  48.             c = c - d * m * 10 ^ p
  49.             a = a + m * 10 ^ p
  50.         END IF
  51.     WEND
  52.  
  53.     'Now for the decimal expansion isolating the repeating part if one
  54.     IF c <> 0 THEN
  55.         DIM b(d)
  56.         b$ = "."
  57.         WHILE c <> 0
  58.  
  59.             'emergency bug out!
  60.             loopct = loopct + 1 'loop count should not exceed 1000 for numbers I am testing
  61.             IF loopct > 1000 THEN PRINT "Error: loop too long, bugging out! ": GOTO skip
  62.  
  63.             'track repeats  b() tracks been here once, b2() tracks been here twice
  64.             IF b(c) = 1 THEN 'been here!
  65.                 IF rFlag = 1 THEN 'been here twice!
  66.                     IF b2(c) = 1 THEN EXIT WHILE 'strike 3, we're out of here
  67.                     b2(c) = 1
  68.                 ELSE
  69.                     rFlag = 1
  70.                     DIM b2(d)
  71.                     b$ = b$ + " repeat "
  72.                     b2(c) = 1
  73.                 END IF
  74.             ELSE
  75.                 b(c) = 1
  76.             END IF
  77.  
  78.             'c was last remainder, mult by 10 and see if some m * d > can reduce it
  79.             tc = 10 * c
  80.             flag = 0
  81.             FOR m = 0 TO 9
  82.                 IF ((tc - m * d) >= 0) AND ((tc - (m + 1) * d) < 0) THEN
  83.                     flag = 1: b$ = b$ + LTRIM$(STR$(m))
  84.                     EXIT FOR
  85.                 END IF
  86.             NEXT
  87.             IF flag = 0 THEN b$ = b$ + "0": m = 0
  88.             c = tc - d * m
  89.         WEND
  90.     END IF
  91.  
  92.     'OK either d divided n eventually or there is a repeated pattern recorded in b$
  93.     skip: '< needed for debugging
  94.     r$ = STR$(a)
  95.     IF b$ <> "" THEN r$ = r$ + b$
  96.     PRINT "Check string before chopping redundant section:"
  97.     PRINT r$
  98.     pete$ = " repeat "
  99.     rpos = INSTR(r$, pete$)
  100.     IF rpos > 0 THEN 'cut string down
  101.         tail$ = MID$(r$, rpos)
  102.         backup = LEN(tail$) - LEN(pete$)
  103.         r$ = MID$(r$, 1, rpos - 1 - backup) + "R" + MID$(r$, rpos - backup, backup)
  104.     END IF
  105.     divide$ = r$
  106.  
  107.  

Yeah, 1/97 is looking like it would need string math to process. :D yikes! that's long!
« Last Edit: April 24, 2019, 11:42:37 am by bplus »