QB64.org Forum

Active Forums => Programs => Topic started by: bplus on April 22, 2019, 11:34:25 am

Title: Convert Decimal to Fraction
Post by: bplus 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.
Title: Re: Convert Decimal to Fraction
Post by: Pete 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
Title: Re: Convert Decimal to Fraction
Post by: Jack002 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

Title: Re: Convert Decimal to Fraction
Post by: bplus 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
Title: Re: Convert Decimal to Fraction
Post by: bplus 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.  
Title: Re: Convert Decimal to Fraction
Post by: Pete 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
Title: Re: Convert Decimal to Fraction
Post by: bplus 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
Title: Re: Convert Decimal to Fraction
Post by: Pete on April 23, 2019, 10:10:48 pm
"I never promised you a rose garden..."

Pete :D
Title: Re: Convert Decimal to Fraction
Post by: Jack002 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.
Title: Re: Convert Decimal to Fraction
Post by: Pete 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
Title: Re: Convert Decimal to Fraction
Post by: Qwerkey 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.
Title: Re: Convert Decimal to Fraction
Post by: bplus 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. :)
Title: Re: Convert Decimal to Fraction
Post by: Jack002 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

;)

Title: Re: Convert Decimal to Fraction
Post by: bplus 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.

Title: Re: Convert Decimal to Fraction
Post by: bplus 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!
Title: Re: Convert Decimal to Fraction
Post by: Pete on April 24, 2019, 11:42:50 am
Funny, never thought of myself as a string variable. I'm more of a numeric type, although I do get parsed off at times.

Pete
Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 24, 2019, 04:14:59 pm
That's wonderful. Basically what I wrote in CLISP many years ago.
Well done.
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 24, 2019, 09:10:04 pm
R notation changed back to the redundant " repeat " form. This makes it easier to convert the R notation decimal number back to the fraction form if the repeat section is not too long (= over 14 digits).

The trick to converting a repeating decimal number to fraction is to align a second repeating section over the first by multiplying by some power of 10, p,  = length of repeating section. This way when you subtract  x * 10 ^ p - x = a definite finite decimal, d st.

x * (10 ^ p - 1) = d
then x = d / (10 ^ p - 1) a fraction!

QB64 can only process p up to around 14 digits in the repeating section of the decimal.

Code: QB64: [Select]
  1. _TITLE "n slash d to R notation and back again" '2019-04-24
  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 immediately 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. '2019-04-24
  14. ' just for kicks, convert the R notation back to fraction if possible.
  15. ' Had to revert back to the redundant " repeat " form of R notation.
  16.  
  17. DEFLNG A-Z
  18.     PRINT: PRINT "Enter 2 integers < 3200, numerator / denominator, 0's quit, don't forget / "
  19.     INPUT nd$
  20.     slash = INSTR(nd$, "/")
  21.     IF slash THEN
  22.         dvsr = VAL(MID$(nd$, slash + 1))
  23.         IF dvsr = 0 THEN PRINT "Divisor is 0, bye.": END
  24.         numerator = VAL(MID$(nd$, 1, slash - 1))
  25.         IF numerator = 0 THEN PRINT "Numerator is 0, bye.": END
  26.     ELSE
  27.         PRINT "No slash found, bye.": END
  28.     END IF
  29.     CLS
  30.     d$ = divide$(numerator, dvsr)
  31.     PRINT numerator; " / "; dvsr; " = "; d$
  32.  
  33.     'and now arttemp"t to convert back to fraction
  34.     PRINT: PRINT "Check if can convert back to fraction:"
  35.     result$ = convertRnotation2Fraction$(d$)
  36.     IF result$ <> "" THEN PRINT result$
  37.  
  38. FUNCTION convertRnotation2Fraction$ (rNotedDecimal$)
  39.     'check if R in the decimal
  40.     dotPos = INSTR(rNotedDecimal$, ".")
  41.     IF dotPos = 0 THEN convertRnotation2Fraction$ = rNotedDecimal$: EXIT FUNCTION
  42.     RPos = INSTR(rNotedDecimal$, " repeat ")
  43.     IF RPos = 0 THEN convertRnotation2Fraction$ = convert2Fraction$(rNotedDecimal$): EXIT FUNCTION
  44.  
  45.     'still here? we have an R and a decimal
  46.     whole$ = MID$(rNotedDecimal$, 1, dotPos - 1)
  47.     IF VAL(whole$) = 0 THEN whole$ = ""
  48.  
  49.     p = LEN(rNotedDecimal$) - RPos - LEN(" repeat ") + 1
  50.     PRINT "Debug: repeat length ="; p
  51.     IF p > 15 THEN
  52.         PRINT "The length of the repeat section of: "
  53.         PRINT rNotedDecimal$
  54.         PRINT " is too long to convert back to fraction."
  55.         EXIT FUNCTION
  56.     END IF
  57.  
  58.     dec$ = MID$(rNotedDecimal$, dotPos)
  59.     PRINT "Debug: converting dec$ "; dec$
  60.  
  61.     'remove " repeat "
  62.     RPos = INSTR(dec$, " repeat ")
  63.     dec1$ = MID$(dec$, 1, RPos - 1) + MID$(dec$, RPos + LEN(" repeat "))
  64.     dec2$ = MID$(dec$, 1, RPos - 1)
  65.     PRINT "Debug: dec1$ (double repeat), dec2$ (single repeat) = "; dec1$; ", "; dec2$
  66.  
  67.     'mult by 10^p to get the 2nd repeat part in dec1$ aligned to 1st repeat part  in dec2$
  68.     vd1## = VAL(dec1$) * 10 ^ p
  69.     vd2## = VAL(dec2$)
  70.     n## = vd1## - vd2## 'subtract dec2$ from dec1$
  71.  
  72.     adj&& = 1 'convert to whole numbers
  73.     WHILE n## <> INT(n##)
  74.         adj&& = adj&& * 10
  75.         n## = n## * 10
  76.     WEND
  77.  
  78.     'reevaluate to avoid rounding errors from crazy floating point math
  79.     n1&& = vd1## * adj&& - vd2## * adj&&
  80.     PRINT "Debug values: vd1, vd2, adj&&, n1&& (difference * adj&& for whole number):"
  81.     PRINT vd1##; ", "; vd2##; ", "; adj&&; ", "; n1&&
  82.  
  83.     d&& = (10 ^ p - 1) * adj&&
  84.     PRINT "Debug: Giant numerator, denominator "; n1&&; ", "; d&& 'giant numbers
  85.  
  86.     'reduce giant numbers by Gretaest Common Divisor between them
  87.     g&& = gcd&&(n1&&, d&&): sn&& = n1&& / g&&: sd&& = d&& / g&&
  88.  
  89.     convertRnotation2Fraction$ = whole$ + " " + _TRIM$(STR$(sn&&)) + "/" + _TRIM$(STR$(sd&&))
  90.  
  91. FUNCTION convert2Fraction$ (decimal$)
  92.     dot%% = INSTR(decimal$, ".")
  93.     IF dot%% > 0 THEN
  94.         whole$ = MID$(decimal$, 1, dot%% - 1)
  95.         IF VAL(whole$) = 0 THEN whole$ = ""
  96.         p%% = LEN(decimal$) - dot%%
  97.         n&& = VAL(MID$(decimal$, dot%% + 1))
  98.         d&& = 10 ^ p%%
  99.         g&& = gcd&&(n&&, d&&): sn&& = n&& / g&&: sd&& = d&& / g&&
  100.         convert2Fraction$ = whole$ + " " + _TRIM$(STR$(sn&&)) + "/" + _TRIM$(STR$(sd&&))
  101.     ELSE
  102.         convert2Fraction$ = decimal$
  103.     END IF
  104.  
  105. FUNCTION gcd&& (a&&, b&&)
  106.     'a and b will be changed unless make copies
  107.     c&& = a&&: d&& = b&&
  108.     WHILE c&& <> 0 AND d&& <> 0
  109.         IF c&& > d&& THEN c&& = c&& MOD d&& ELSE d&& = d&& MOD c&&
  110.     WEND
  111.     gcd&& = c&& + d&&
  112.  
  113.  
  114. FUNCTION divide$ (n, d)
  115.     'n = original product or numerator (preserve value of n)
  116.     'd = divisor  (also preserve value)
  117.     c = n 'copy of n to be reduced until <= d, c will be the remainder part of division
  118.     a = 0 'a is for answer or accumulate, the integer part of the division result
  119.  
  120.     'find lowest power of 10 such that: d * 10^p > n
  121.     p = 0 'power of 10
  122.     WHILE d * (10 ^ p) < n
  123.         p = p + 1
  124.     WEND
  125.     WHILE c >= d
  126.         IF c = d THEN a = a + 1: c = 0: EXIT WHILE
  127.         p = p - 1
  128.         IF p >= 0 THEN
  129.             m = 0
  130.             WHILE d * m * 10 ^ p < c
  131.                 m = m + 1
  132.             WEND
  133.             m = m - 1
  134.             c = c - d * m * 10 ^ p
  135.             a = a + m * 10 ^ p
  136.         END IF
  137.     WEND
  138.  
  139.     'Now for the decimal expansion isolating the repeating part if one
  140.     IF c <> 0 THEN
  141.         DIM b(d)
  142.         b$ = "."
  143.         WHILE c <> 0
  144.  
  145.             'emergency bug out!
  146.             loopct = loopct + 1 'loop count should not exceed 1000 for numbers I am testing
  147.             IF loopct > 1000 THEN PRINT "Error: loop too long, bugging out! ": GOTO skip
  148.  
  149.             'track repeats  b() tracks been here once, b2() tracks been here twice
  150.             IF b(c) = 1 THEN 'been here!
  151.                 IF rFlag = 1 THEN 'been here twice!
  152.                     IF b2(c) = 1 THEN EXIT WHILE 'strike 3, we're out of here
  153.                     b2(c) = 1
  154.                 ELSE
  155.                     rFlag = 1
  156.                     DIM b2(d)
  157.                     b$ = b$ + " repeat "
  158.                     b2(c) = 1
  159.                 END IF
  160.             ELSE
  161.                 b(c) = 1
  162.             END IF
  163.  
  164.             'c was last remainder, mult by 10 and see if some m * d > can reduce it
  165.             tc = 10 * c
  166.             flag = 0
  167.             FOR m = 0 TO 9
  168.                 IF ((tc - m * d) >= 0) AND ((tc - (m + 1) * d) < 0) THEN
  169.                     flag = 1: b$ = b$ + LTRIM$(STR$(m))
  170.                     EXIT FOR
  171.                 END IF
  172.             NEXT
  173.             IF flag = 0 THEN b$ = b$ + "0": m = 0
  174.             c = tc - d * m
  175.         WEND
  176.     END IF
  177.  
  178.     'OK either d divided n eventually or there is a repeated pattern recorded in b$
  179.     skip: '< needed for debugging
  180.     r$ = STR$(a)
  181.     IF b$ <> "" THEN r$ = r$ + b$
  182.     divide$ = r$
  183.  
  184.  
Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 25, 2019, 11:20:08 am
I crashed it with 1/62
[edit]
No, I think its just me. I am not on the new version yet. I replaced _trim$ with ltrim$. Seems that didn't work.
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 25, 2019, 12:00:43 pm
I crashed it with 1/62
[edit]
No, I think its just me. I am not on the new version yet. I replaced _trim$ with ltrim$. Seems that didn't work.

Hi Jack002,

Oh! looks like I was allowing repeat sections up to 15 because 1/62 was 15. OK on my system QB64 v1.3 on Windows 10,   the code works and arrives back at 1/62.
Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 25, 2019, 04:11:13 pm
I'm on V 1.2, I need to get the new 1.3 version installed.
(all the posts here seem to use the new functions of 1.3, so this version is no good for any of those)
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 25, 2019, 04:21:08 pm
I'm on V 1.2, I need to get the new 1.3 version installed.
(all the posts here seem to use the new functions of 1.3, so this version is no good for any of those)


I think _TRIM$ is the only new function I am using from v1.3 and replacing it with LTRIM$ should be fine, the only thing it is doing is formatting the output display, it shouldn't interfere with main code. (Though I don't want to discourage you from updating. :)

Jack002 are you using Windows 10 for OS?
Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 25, 2019, 10:59:12 pm
Yes, windows 10 and I changed _trim$ to ltrim$ and it fails.
Title: Re: Convert Decimal to Fraction
Post by: FellippeHeitor on April 25, 2019, 11:12:29 pm
_TRIM$() stands for RTRIM$(LTRIM$()). Try that.
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 26, 2019, 10:21:01 am
It's not _trim$ that is causing problem for 1/62 because, I changed them to LTRIM$ and get same results with extra space on right in display.
 


A variable type is different, I can see that in the negative numbers in Jack002's last line of output and also adj&& is different, I have 10, Jack002s' code has way more for adj&&. Perhaps Jack002 changed more than _TRIM$??

I'd like to compare the code Jack002 is running with mine through WinMerge.

Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 26, 2019, 04:51:46 pm
No, all I changed was trim. Here it is just how I ran it
[edit]
I just now changed all my ltrims to rtrim$(ltrim$()), no change. It still does the same thing
Code: QB64: [Select]
  1. _TITLE "n slash d to R notation and back again" '2019-04-24
  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 immediately 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. '2019-04-24
  14. ' just for kicks, convert the R notation back to fraction if possible.
  15. ' Had to revert back to the redundant " repeat " form of R notation.
  16.  
  17. DEFLNG A-Z
  18.     PRINT: PRINT "Enter 2 integers < 3200, numerator / denominator, 0's quit, don't forget / "
  19.     INPUT nd$
  20.     slash = INSTR(nd$, "/")
  21.     IF slash THEN
  22.         dvsr = VAL(MID$(nd$, slash + 1))
  23.         IF dvsr = 0 THEN PRINT "Divisor is 0, bye.": END
  24.         numerator = VAL(MID$(nd$, 1, slash - 1))
  25.         IF numerator = 0 THEN PRINT "Numerator is 0, bye.": END
  26.     ELSE
  27.         PRINT "No slash found, bye.": END
  28.     END IF
  29.     CLS
  30.     d$ = divide$(numerator, dvsr)
  31.     PRINT numerator; " / "; dvsr; " = "; d$
  32.  
  33.     'and now arttemp"t to convert back to fraction
  34.     PRINT: PRINT "Check if can convert back to fraction:"
  35.     result$ = convertRnotation2Fraction$(d$)
  36.     IF result$ <> "" THEN PRINT result$
  37.  
  38. FUNCTION convertRnotation2Fraction$ (rNotedDecimal$)
  39.     'check if R in the decimal
  40.     dotPos = INSTR(rNotedDecimal$, ".")
  41.     IF dotPos = 0 THEN convertRnotation2Fraction$ = rNotedDecimal$: EXIT FUNCTION
  42.     RPos = INSTR(rNotedDecimal$, " repeat ")
  43.     IF RPos = 0 THEN convertRnotation2Fraction$ = convert2Fraction$(rNotedDecimal$): EXIT FUNCTION
  44.  
  45.     'still here? we have an R and a decimal
  46.     whole$ = MID$(rNotedDecimal$, 1, dotPos - 1)
  47.     IF VAL(whole$) = 0 THEN whole$ = ""
  48.  
  49.     p = LEN(rNotedDecimal$) - RPos - LEN(" repeat ") + 1
  50.     PRINT "Debug: repeat length ="; p
  51.     IF p > 15 THEN
  52.         PRINT "The length of the repeat section of: "
  53.         PRINT rNotedDecimal$
  54.         PRINT " is too long to convert back to fraction."
  55.         EXIT FUNCTION
  56.     END IF
  57.  
  58.     dec$ = MID$(rNotedDecimal$, dotPos)
  59.     PRINT "Debug: converting dec$ "; dec$
  60.  
  61.     'remove " repeat "
  62.     RPos = INSTR(dec$, " repeat ")
  63.     dec1$ = MID$(dec$, 1, RPos - 1) + MID$(dec$, RPos + LEN(" repeat "))
  64.     dec2$ = MID$(dec$, 1, RPos - 1)
  65.     PRINT "Debug: dec1$ (double repeat), dec2$ (single repeat) = "; dec1$; ", "; dec2$
  66.  
  67.     'mult by 10^p to get the 2nd repeat part in dec1$ aligned to 1st repeat part  in dec2$
  68.     vd1## = VAL(dec1$) * 10 ^ p
  69.     vd2## = VAL(dec2$)
  70.     n## = vd1## - vd2## 'subtract dec2$ from dec1$
  71.  
  72.     adj&& = 1 'convert to whole numbers
  73.     WHILE n## <> INT(n##)
  74.         adj&& = adj&& * 10
  75.         n## = n## * 10
  76.     WEND
  77.  
  78.     'reevaluate to avoid rounding errors from crazy floating point math
  79.     n1&& = vd1## * adj&& - vd2## * adj&&
  80.     PRINT "Debug values: vd1, vd2, adj&&, n1&& (difference * adj&& for whole number):"
  81.     PRINT vd1##; ", "; vd2##; ", "; adj&&; ", "; n1&&
  82.  
  83.     d&& = (10 ^ p - 1) * adj&&
  84.     PRINT "Debug: Giant numerator, denominator "; n1&&; ", "; d&& 'giant numbers
  85.  
  86.     'reduce giant numbers by Gretaest Common Divisor between them
  87.     g&& = gcd&&(n1&&, d&&): sn&& = n1&& / g&&: sd&& = d&& / g&&
  88.  
  89.     convertRnotation2Fraction$ = whole$ + " " + LTRIM$(STR$(sn&&)) + "/" + LTRIM$(STR$(sd&&))
  90.  
  91. FUNCTION convert2Fraction$ (decimal$)
  92.     dot%% = INSTR(decimal$, ".")
  93.     IF dot%% > 0 THEN
  94.         whole$ = MID$(decimal$, 1, dot%% - 1)
  95.         IF VAL(whole$) = 0 THEN whole$ = ""
  96.         p%% = LEN(decimal$) - dot%%
  97.         n&& = VAL(MID$(decimal$, dot%% + 1))
  98.         d&& = 10 ^ p%%
  99.         g&& = gcd&&(n&&, d&&): sn&& = n&& / g&&: sd&& = d&& / g&&
  100.         convert2Fraction$ = whole$ + " " + LTRIM$(STR$(sn&&)) + "/" + LTRIM$(STR$(sd&&))
  101.     ELSE
  102.         convert2Fraction$ = decimal$
  103.     END IF
  104.  
  105. FUNCTION gcd&& (a&&, b&&)
  106.     'a and b will be changed unless make copies
  107.     c&& = a&&: d&& = b&&
  108.     WHILE c&& <> 0 AND d&& <> 0
  109.         IF c&& > d&& THEN c&& = c&& MOD d&& ELSE d&& = d&& MOD c&&
  110.     WEND
  111.     gcd&& = c&& + d&&
  112.  
  113.  
  114. FUNCTION divide$ (n, d)
  115.     'n = original product or numerator (preserve value of n)
  116.     'd = divisor  (also preserve value)
  117.     c = n 'copy of n to be reduced until <= d, c will be the remainder part of division
  118.     a = 0 'a is for answer or accumulate, the integer part of the division result
  119.  
  120.     'find lowest power of 10 such that: d * 10^p > n
  121.     p = 0 'power of 10
  122.     WHILE d * (10 ^ p) < n
  123.         p = p + 1
  124.     WEND
  125.     WHILE c >= d
  126.         IF c = d THEN a = a + 1: c = 0: EXIT WHILE
  127.         p = p - 1
  128.         IF p >= 0 THEN
  129.             m = 0
  130.             WHILE d * m * 10 ^ p < c
  131.                 m = m + 1
  132.             WEND
  133.             m = m - 1
  134.             c = c - d * m * 10 ^ p
  135.             a = a + m * 10 ^ p
  136.         END IF
  137.     WEND
  138.  
  139.     'Now for the decimal expansion isolating the repeating part if one
  140.     IF c <> 0 THEN
  141.         DIM b(d)
  142.         b$ = "."
  143.         WHILE c <> 0
  144.  
  145.             'emergency bug out!
  146.             loopct = loopct + 1 'loop count should not exceed 1000 for numbers I am testing
  147.             IF loopct > 1000 THEN PRINT "Error: loop too long, bugging out! ": GOTO skip
  148.  
  149.             'track repeats  b() tracks been here once, b2() tracks been here twice
  150.             IF b(c) = 1 THEN 'been here!
  151.                 IF rFlag = 1 THEN 'been here twice!
  152.                     IF b2(c) = 1 THEN EXIT WHILE 'strike 3, we're out of here
  153.                     b2(c) = 1
  154.                 ELSE
  155.                     rFlag = 1
  156.                     DIM b2(d)
  157.                     b$ = b$ + " repeat "
  158.                     b2(c) = 1
  159.                 END IF
  160.             ELSE
  161.                 b(c) = 1
  162.             END IF
  163.  
  164.             'c was last remainder, mult by 10 and see if some m * d > can reduce it
  165.             tc = 10 * c
  166.             flag = 0
  167.             FOR m = 0 TO 9
  168.                 IF ((tc - m * d) >= 0) AND ((tc - (m + 1) * d) < 0) THEN
  169.                     flag = 1: b$ = b$ + LTRIM$(STR$(m))
  170.                     EXIT FOR
  171.                 END IF
  172.             NEXT
  173.             IF flag = 0 THEN b$ = b$ + "0": m = 0
  174.             c = tc - d * m
  175.         WEND
  176.     END IF
  177.  
  178.     'OK either d divided n eventually or there is a repeated pattern recorded in b$
  179.     skip: '< needed for debugging
  180.     r$ = STR$(a)
  181.     IF b$ <> "" THEN r$ = r$ + b$
  182.     divide$ = r$
  183.  
  184.  
  185.  
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 26, 2019, 05:19:41 pm
Thanks for your reply Jack002.

Yeah, I run very same and do get back to 1/62 on last line.

Don't know what our difference is????
Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 26, 2019, 10:17:56 pm
I blame Obama

LOL
[edit]

It fails on this line
 g&& = gcd&&(n1&&, d&&)
I don't follow all the logic here, once you make vd1 and vd2 etc you start to lose me.
Does it matter that the value of vd1 is not an integer? Its 16129032258064.52
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 27, 2019, 12:18:58 am
VD1 is not an integer until we multiply it and VD2 by adj&&

Here is an example of converting 1/6 in English:
Quote
Convert a repeating decimal back to fraction:

1/6 = .16R6  = x

10x = 1.666...   VD1
- x =    .166...   VD2
=============
9x   = 1.5      VD1 - VD2

adj = 10  a multiplier to get rid of decimal

90x = 15 or x = 15 /90

GCD(15, 90) = greatest common denominator of 15 and 90 is 15

15/15 = 1   and 90/15 = 6 so 15/90 = 1/6

x = 1/6
Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 27, 2019, 09:17:23 am
Ok so vd1 is larger than vd2. You multiply both by adj and take a difference. Won't that number be positive? If so, why is this negative?
  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 27, 2019, 09:22:01 am
After some more experimenting I see it also fails for 1/60. GCD is given two numbers, one is negative. It seems to always happen when one or both numbers given GCD are negative
{1/56 also fails for the same reason, both numbers given to GCD are negative}
 
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 27, 2019, 09:37:27 am
Ok so vd1 is larger than vd2. You multiply both by adj and take a difference. Won't that number be positive? If so, why is this negative?
 

Yes why are your numerator and denominator negative? The only time I see that is when the number exceeds it's TYPE limit.

And why is your adj 1000000 when mine is 10? That might make VD1 and VD2 too big even for _INTEGER64 limit.

So the crux of our difference might begin here with the calculation of adj&&
Code: QB64: [Select]
  1.     adj&& = 1 'convert to whole numbers
  2.     WHILE n## <> INT(n##)
  3.         adj&& = adj&& * 10
  4.         n## = n## * 10
  5.     WEND
  6.  
adj&& is the multiplier needed to get VD1-VD2 (which becomes the numerator of the fraction) into a whole number.

We are using the same code. Is there a version 1.3 difference in handling the variable types?




Has anyone else tried 1/62? What do you get with what version of QB64?


OK I am still getting good results with Jack002 code on an older version of QB64.
 
 

Jack002 are you using 32 bit of even X86 versions of QB64?
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 27, 2019, 10:23:57 am
After some more experimenting I see it also fails for 1/60. GCD is given two numbers, one is negative. It seems to always happen when one or both numbers given GCD are negative
{1/56 also fails for the same reason, both numbers given to GCD are negative}
 


The 3rd number from left of the 2nd line from bottom adj&& is supposed to be a simple power of 10, eg 10, 100, 1000, 10000...
Code: QB64: [Select]
  1.     adj&& = 1 'convert to whole numbers
  2.     WHILE n## <> INT(n##)
  3.         adj&& = adj&& * 10
  4.         n## = n## * 10
  5.     WEND

So there is a type limit problem.
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 27, 2019, 10:39:58 am
1/56 works perfectly:
 


1/60 has a suspiciously high adj&&
 
Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 27, 2019, 05:32:17 pm
The while loop that makes ADJ has issues. I ran this test script and its not seeing it's done?! Hello? McFly!?
I don't know why this is failing. The number and the INT are the same.
Code: QB64: [Select]
  1. vd1## = .166
  2. vd2## = .016
  3.  
  4. n## = vd1## - vd2## 'subtract dec2$ from dec1$
  5.  
  6. PRINT "vd1"; vd1##
  7. PRINT "vd2"; vd2##
  8. PRINT "n##"; n##
  9.  
  10. adj&& = 1 'convert to whole numbers
  11. WHILE n## <> INT(n##)
  12.     adj&& = adj&& * 10
  13.     PRINT "adj"; adj&&
  14.     n## = n## * 10
  15.     PRINT "test, is "; n##; " = "; INT(n##); "?"
  16.     SLEEP
  17.  

I see it keeps going till ADJ is 1.5D+19!!

Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 27, 2019, 05:39:00 pm
I see some kind of rounding error here. I added a difference between n## and int(n##)  and things get funny
Code: QB64: [Select]
  1. vd1## = .166
  2. vd2## = .016
  3.  
  4. n## = vd1## - vd2## 'subtract dec2$ from dec1$
  5.  
  6. PRINT "vd1"; vd1##
  7. PRINT "vd2"; vd2##
  8. PRINT "n##"; n##
  9.  
  10. adj&& = 1 'convert to whole numbers
  11. WHILE n## <> INT(n##)
  12.     adj&& = adj&& * 10
  13.     PRINT "adj"; adj&&
  14.     n## = n## * 10
  15.     PRINT "test, is "; n##; " = "; INT(n##); "?"
  16.     PRINT n## - INT(n##)
  17.     SLEEP
  18.  


EDIT

I made two changes to the while loop. Now it seems to work

Code: QB64: [Select]
  1.     WHILE (n## - INT(n##)) > 1.0D-15
  2.         adj&& = adj&& * 10
  3.         n## = n## * 10 + 1.0D+15
  4.         PRINT n##; INT(n##); (n## - INT(n##)); adj&&
  5.         SLEEP
  6.     WEND
  7.  
I have to add 1.0D-15 to n## so it won't keep drifting.
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 27, 2019, 08:46:33 pm
Hi Jack002,

Yep! I was convinced something was screwy when I saw the adj&& for 1/60.

Here is another way around adj&& calc:
Code: QB64: [Select]
  1.     'adj&& = 1 'convert to whole numbers
  2.     'WHILE n## <> INT(n##)
  3.     '    adj&& = adj&& * 10
  4.     '    n## = n## * 10
  5.     'WEND
  6.  
  7.     ns$ = STR$(n##)
  8.     dot = INSTR(ns$, ".")
  9.     p2 = LEN(ns$) - dot
  10.     adj&& = 10 ^ p2
  11.  

I also had to reduce max length of the repeat section below 13 because 1/79 was getting negative numerator and denominator like you were getting, which means the type limit was exceeded.

Modified code:
Code: QB64: [Select]
  1. _TITLE "n slash d to R notation and back again" '2019-04-24
  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 immediately 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. '2019-04-24
  14. ' just for kicks, convert the R notation back to fraction if possible.
  15. ' Had to revert back to the redundant " repeat " form of R notation.
  16.  
  17. '2019-04-27 Thanks to Jack002's comments and replies this code to calculate adj&& may have been improved.
  18.  
  19. DEFLNG A-Z
  20.     PRINT: PRINT "Enter 2 integers < 3200, numerator / denominator, 0's quit, don't forget / "
  21.     INPUT nd$
  22.     slash = INSTR(nd$, "/")
  23.     IF slash THEN
  24.         dvsr = VAL(MID$(nd$, slash + 1))
  25.         IF dvsr = 0 THEN PRINT "Divisor is 0, bye.": END
  26.         numerator = VAL(MID$(nd$, 1, slash - 1))
  27.         IF numerator = 0 THEN PRINT "Numerator is 0, bye.": END
  28.     ELSE
  29.         PRINT "No slash found, bye.": END
  30.     END IF
  31.     CLS
  32.     d$ = divide$(numerator, dvsr)
  33.     PRINT numerator; " / "; dvsr; " = "; d$
  34.  
  35.     'and now arttemp"t to convert back to fraction
  36.     PRINT: PRINT "Check if can convert back to fraction:"
  37.     result$ = convertRnotation2Fraction$(d$)
  38.     IF result$ <> "" THEN PRINT result$
  39.  
  40. FUNCTION convertRnotation2Fraction$ (rNotedDecimal$)
  41.     'check if R in the decimal
  42.     dotPos = INSTR(rNotedDecimal$, ".")
  43.     IF dotPos = 0 THEN convertRnotation2Fraction$ = rNotedDecimal$: EXIT FUNCTION
  44.     RPos = INSTR(rNotedDecimal$, " repeat ")
  45.     IF RPos = 0 THEN convertRnotation2Fraction$ = convert2Fraction$(rNotedDecimal$): EXIT FUNCTION
  46.  
  47.     'still here? we have an R and a decimal
  48.     whole$ = MID$(rNotedDecimal$, 1, dotPos - 1)
  49.     IF VAL(whole$) = 0 THEN whole$ = ""
  50.  
  51.     p = LEN(rNotedDecimal$) - RPos - LEN(" repeat ") + 1
  52.     PRINT "Debug: repeat length ="; p
  53.     IF p > 12 THEN
  54.         PRINT "The length of the repeat section of: "
  55.         PRINT rNotedDecimal$
  56.         PRINT " is too long to convert back to fraction."
  57.         EXIT FUNCTION
  58.     END IF
  59.  
  60.     dec$ = MID$(rNotedDecimal$, dotPos)
  61.     PRINT "Debug: converting dec$ "; dec$
  62.  
  63.     'remove " repeat "
  64.     RPos = INSTR(dec$, " repeat ")
  65.     dec1$ = MID$(dec$, 1, RPos - 1) + MID$(dec$, RPos + LEN(" repeat "))
  66.     dec2$ = MID$(dec$, 1, RPos - 1)
  67.     PRINT "Debug: dec1$ (double repeat), dec2$ (single repeat) = "; dec1$; ", "; dec2$
  68.  
  69.     'mult by 10^p to get the 2nd repeat part in dec1$ aligned to 1st repeat part  in dec2$
  70.     vd1## = VAL(dec1$) * 10 ^ p
  71.     vd2## = VAL(dec2$)
  72.     n## = vd1## - vd2## 'subtract dec2$ from dec1$
  73.  
  74.     'adj&& = 1 'convert to whole numbers
  75.     'WHILE n## <> INT(n##)
  76.     '    adj&& = adj&& * 10
  77.     '    n## = n## * 10
  78.     'WEND
  79.  
  80.     'calculate adj&& from length of decimal
  81.     ns$ = STR$(n##)
  82.     dot = INSTR(ns$, ".")
  83.     p2 = LEN(ns$) - dot
  84.     adj&& = 10 ^ p2
  85.  
  86.     'reevaluate to avoid rounding errors from crazy floating point math
  87.     n1&& = vd1## * adj&& - vd2## * adj&&
  88.     PRINT "Debug values: vd1, vd2, adj&&, n1&& (difference * adj&& for whole number):"
  89.     PRINT vd1##; ", "; vd2##; ", "; adj&&; ", "; n1&&
  90.  
  91.     d&& = (10 ^ p - 1) * adj&&
  92.     PRINT "Debug: Giant numerator, denominator "; n1&&; ", "; d&& 'giant numbers
  93.  
  94.     'reduce giant numbers by Gretaest Common Divisor between them
  95.     g&& = gcd&&(n1&&, d&&): sn&& = n1&& / g&&: sd&& = d&& / g&&
  96.  
  97.     convertRnotation2Fraction$ = whole$ + " " + _TRIM$(STR$(sn&&)) + "/" + _TRIM$(STR$(sd&&))
  98.  
  99. FUNCTION convert2Fraction$ (decimal$)
  100.     dot%% = INSTR(decimal$, ".")
  101.     IF dot%% > 0 THEN
  102.         whole$ = MID$(decimal$, 1, dot%% - 1)
  103.         IF VAL(whole$) = 0 THEN whole$ = ""
  104.         p%% = LEN(decimal$) - dot%%
  105.         n&& = VAL(MID$(decimal$, dot%% + 1))
  106.         d&& = 10 ^ p%%
  107.         g&& = gcd&&(n&&, d&&): sn&& = n&& / g&&: sd&& = d&& / g&&
  108.         convert2Fraction$ = whole$ + " " + _TRIM$(STR$(sn&&)) + "/" + _TRIM$(STR$(sd&&))
  109.     ELSE
  110.         convert2Fraction$ = decimal$
  111.     END IF
  112.  
  113. FUNCTION gcd&& (a&&, b&&)
  114.     'a and b will be changed unless make copies
  115.     c&& = a&&: d&& = b&&
  116.     WHILE c&& <> 0 AND d&& <> 0
  117.         IF c&& > d&& THEN c&& = c&& MOD d&& ELSE d&& = d&& MOD c&&
  118.     WEND
  119.     gcd&& = c&& + d&&
  120.  
  121.  
  122. FUNCTION divide$ (n, d)
  123.     'n = original product or numerator (preserve value of n)
  124.     'd = divisor  (also preserve value)
  125.     c = n 'copy of n to be reduced until <= d, c will be the remainder part of division
  126.     a = 0 'a is for answer or accumulate, the integer part of the division result
  127.  
  128.     'find lowest power of 10 such that: d * 10^p > n
  129.     p = 0 'power of 10
  130.     WHILE d * (10 ^ p) < n
  131.         p = p + 1
  132.     WEND
  133.     WHILE c >= d
  134.         IF c = d THEN a = a + 1: c = 0: EXIT WHILE
  135.         p = p - 1
  136.         IF p >= 0 THEN
  137.             m = 0
  138.             WHILE d * m * 10 ^ p < c
  139.                 m = m + 1
  140.             WEND
  141.             m = m - 1
  142.             c = c - d * m * 10 ^ p
  143.             a = a + m * 10 ^ p
  144.         END IF
  145.     WEND
  146.  
  147.     'Now for the decimal expansion isolating the repeating part if one
  148.     IF c <> 0 THEN
  149.         DIM b(d)
  150.         b$ = "."
  151.         WHILE c <> 0
  152.  
  153.             'emergency bug out!
  154.             loopct = loopct + 1 'loop count should not exceed 1000 for numbers I am testing
  155.             IF loopct > 1000 THEN PRINT "Error: loop too long, bugging out! ": GOTO skip
  156.  
  157.             'track repeats  b() tracks been here once, b2() tracks been here twice
  158.             IF b(c) = 1 THEN 'been here!
  159.                 IF rFlag = 1 THEN 'been here twice!
  160.                     IF b2(c) = 1 THEN EXIT WHILE 'strike 3, we're out of here
  161.                     b2(c) = 1
  162.                 ELSE
  163.                     rFlag = 1
  164.                     DIM b2(d)
  165.                     b$ = b$ + " repeat "
  166.                     b2(c) = 1
  167.                 END IF
  168.             ELSE
  169.                 b(c) = 1
  170.             END IF
  171.  
  172.             'c was last remainder, mult by 10 and see if some m * d > can reduce it
  173.             tc = 10 * c
  174.             flag = 0
  175.             FOR m = 0 TO 9
  176.                 IF ((tc - m * d) >= 0) AND ((tc - (m + 1) * d) < 0) THEN
  177.                     flag = 1: b$ = b$ + LTRIM$(STR$(m))
  178.                     EXIT FOR
  179.                 END IF
  180.             NEXT
  181.             IF flag = 0 THEN b$ = b$ + "0": m = 0
  182.             c = tc - d * m
  183.         WEND
  184.     END IF
  185.  
  186.     'OK either d divided n eventually or there is a repeated pattern recorded in b$
  187.     skip: '< needed for debugging
  188.     r$ = STR$(a)
  189.     IF b$ <> "" THEN r$ = r$ + b$
  190.     divide$ = r$
  191.  
Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 28, 2019, 12:19:01 am
I was proud to hack some kind of solution. I had my doubts. I have written one like your first part that does the dec equivalent, but never thought to make it go back again. Well done (in spite of any problems)

Why do you suppose there is so much rounding error between the versions? This is like the old 8 bit days when you do 1/3 and then * 3 and get this long line of 9s. I know I saw that on the commodore 64.
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 28, 2019, 09:45:54 am
I was proud to hack some kind of solution. I had my doubts. I have written one like your first part that does the dec equivalent, but never thought to make it go back again. Well done (in spite of any problems)

Why do you suppose there is so much rounding error between the versions? This is like the old 8 bit days when you do 1/3 and then * 3 and get this long line of 9s. I know I saw that on the commodore 64.


Hi Jack002,

I am sorry I missed the experience with a Commodore 64, you have obviously been deeply affected by it. I did have experience with GW BASIC and DOS and loved the way the two worked together, then later VB for DOS and DOS great stuff!

The great difference between our different results of this code, I am still attributing to the possibility you are using a 32 bit version of QB64 or even x86. You have not said X64, x32 or even x86 that your version of QB64 is. If you are using a X64 bit version of QB64 I am completely baffled! LOL wouldn't be first time!
Title: Re: Convert Decimal to Fraction
Post by: SMcNeill on April 28, 2019, 12:46:57 pm
Hi Jack002,

I am sorry I missed the experience with a Commodore 64, you have obviously been deeply affected by it. I did have experience with GW BASIC and DOS and loved the way the two worked together, then later VB for DOS and DOS great stuff!

The great difference between our different results of this code, I am still attributing to the possibility you are using a 32 bit version of QB64 or even x86. You have not said X64, x32 or even x86 that your version of QB64 is. If you are using a X64 bit version of QB64 I am completely baffled! LOL wouldn't be first time!

We’ve seen some drastic differences in the past with how Windows does math with 32-bit and 64-bit code, so I’m guessing that’s probably the difference as well. 
Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 28, 2019, 04:15:03 pm
I am on QB64 1.2, I have a 64 bit computer. Its a Dell latitude running windows 10

If we can't expect every computer to make the same calculations in things like this how can we ever plan to write a program that works for everyone? I'm stumped.
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 28, 2019, 05:37:03 pm
Hi Jack002,

Until version 1.3 all stable versions of QB64 were 32 bit, didn't matter if your computer was 64 bit.

Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 28, 2019, 05:39:12 pm
For fun I put the program I made in QB64 to find what the issue was with getting ADJ in the vice C64 emulator. Similar problems, offsets were bigger.
  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: Convert Decimal to Fraction
Post by: Pete on April 28, 2019, 08:19:17 pm
To answer your question, Jack, you would need your program to identify the system it is running on and have algorithms for all possible situations. It's the same PITA that web designers go though these days.

Pete
Title: Re: Convert Decimal to Fraction
Post by: Jack002 on April 28, 2019, 11:55:29 pm
Thanks Pete. Looks like I need to get over to v1.3 right away. These rounding issues are making me thirsty. (Seinfeld)

Title: Re: Convert Decimal to Fraction
Post by: bplus on April 30, 2019, 10:46:48 am
Well in summary, converting back to fractions is very, very limited.

The new method of calculating adj&& is ruined when STR$(n##) produces a string with scientific or exponential notation.
Code: QB64: [Select]
  1.     ns$ = STR$(n##)
  2.     dot = INSTR(ns$, ".")
  3.     p2 = LEN(ns$) - dot
  4.     adj&& = 10 ^ p2
  5.  

Also the longer the string of zeros to the right of the decimal the less repeat length this convert back to fraction can tolerate.

I actually started reconsidering picking up string math to attempt larger fraction conversions but then I would need GCD function for number strings also... just doesn't seem worth the trouble. Like Pete said, who uses such cumbersome fractions anyway?

Just neat to see that they could (if only in theory) be converted back (but not with rounding).

I can see rounding decimals to closest 1/2, 1/4, 1/8, 1/16, 1/32, 1/64 of a unit as being useful.

Title: Re: Convert Decimal to Fraction
Post by: jack on April 30, 2019, 02:24:45 pm
hello bplus
just for fun, expand the decimal to continued fraction convergents https://en.wikipedia.org/wiki/Continued_fraction, see the comments
you will wind up with successive approximations to Pi in this instance
3/1, 22/7, 333/106, 355/113
the only problem is how to know when you reached the optimum
Code: [Select]
DIM n1 AS DOUBLE, n2 AS DOUBLE, n3 AS DOUBLE
DIM d1 AS DOUBLE, d2 AS DOUBLE, d3 AS DOUBLE
DIM fp AS DOUBLE, ip AS DOUBLE

'expand a decimal number to a fraction using continued fraction convergents
'take Pi for instance
'int(Pi) = 3, frac = Pi-3 = .1415926535897932384626433832795
'    | 3
'0 1 |
'1 0 |
'3 * 1 + 0 = 3
'3 * 0 + 1 = 1
'    | 3 |
'0 1 | 3 |
'1 0 | 1 |
'frac = 1/ frac : int(frac) = 7 : frac = frac - 7
'
'7 * 3 + 1 = 22
'7 * 1 + 0 = 7
'    | 3 | 7
'0 1 | 3 | 22
'1 0 | 1 |  7
'frac = 1/ frac : int(frac) = 15 : frac = frac - 15
'
'15 * 22 + 3 = 333
'15 * 7  + 1 = 106
'    | 3 |  7 |  15
'0 1 | 3 | 22 | 333
'1 0 | 1 |  7 | 106
'frac = 1/ frac : int(frac) = 1 : frac = frac - 1
'
'1 * 333 + 22 = 355
'1 * 106 + 7  = 113
'    | 3 |  7 |  15 | 1
'0 1 | 3 | 22 | 333 | 355
'1 0 | 1 |  7 | 106 | 113

n1 = 0: n2 = 1
d1 = 1: d2 = 0
fp = 3.1415926535897932384626433832795
'fp = .125125
FOR i = 1 TO 6
    ip = INT(fp)
    n3 = ip * n2 + n1
    d3 = ip * d2 + d1
    n1 = n2: n2 = n3
    d1 = d2: d2 = d3
    fp = 1 / (fp - ip)
    PRINT n3; "/"; d3
NEXT
Title: Re: Convert Decimal to Fraction
Post by: bplus on April 30, 2019, 03:06:02 pm
Hi jack,

Would that sequence continue to get better, if you could extend the precision of the calculations?
Title: Re: Convert Decimal to Fraction
Post by: jack on April 30, 2019, 04:12:37 pm
yes.