19
\$\begingroup\$
1 REM
2 REM FIZZBUZZ IN COMMODORE BASIC
3 REM
10 FOR I = 1 TO 100
20 IF (I/3)=INT(I/3) THEN PRINT "FIZZ";
30 IF (I/5)=INT(I/5) THEN PRINT "BUZZ";
40 IF (I/3)<>INT(I/3) AND (I/5)<>INT(I/5) THEN PRINT I;
50 PRINT
60 NEXT I
  1. I'm not happy with the tests for "multiple of 3" and "multiple of 5". Using floating-point arithmetic to determine integer multipleness feels highly inelegant, but I don't believe Commodore Basic has "integer mod" functionality, and I can't think of another similarly-compact way to determine that one integer is a multiple of another.
  2. When run using the VICE emulator, it takes almost nine seconds to execute. Besides getting rid of the floating-point division, are there any obvious ways to speed things up?
\$\endgroup\$
2
  • \$\begingroup\$ Commodore BASIC (the real one, anyway) didn't do really integer math at all. It does integer math by converting the integers to floating point, doing the math, and converting the result back to integer, so it was (substantially) slower than just using floating point math. \$\endgroup\$ Commented Jul 13, 2014 at 6:40
  • \$\begingroup\$ Is this related to C64? There are many variants of Commodore BASIC. \$\endgroup\$
    – friol
    Commented Jul 13, 2014 at 13:37

2 Answers 2

15
\$\begingroup\$

As Jerry explained, all math operations treat numbers as floating point... even numbers stored as integers. This was my first attempt:

1 rem
2 rem fizzbuzz in commodore basic
3 rem
10 t = time
20 for i = 1 to 100
30 n = i / 15 :if n = int(n) then print "fizzbuzz" :goto 70
40 n = i / 3 :if n = int(n) then print "fizz" :goto 70
50 n = i / 5 :if n = int(n) then print "buzz" :goto 70
60 print i
70 next
80 print "ran for" (time - t) / 60 "seconds"
  • Skipping the rest of the conditions when a condition passes speeds it up significantly.

  • Assigning the value of X/Y to a variable and reusing it is faster than calculating it twice. Some places around the internet mention the formula X-INT(X/Y)*Y, but that was slower than N=INT(N) in my tests.

  • According to this article you can write your own modulo "operator" in assembly and use it in BASIC. The assembly program is available here. Seems like overkill to me.


You might think that you could replace the goto 70 with next, but it doesn't quite work. Once the loop is complete, it will go to the next line, and call the next on that line, and you'll get a "next without for" error. Technically it still does what it's supposed to do, but it doesn't exit cleanly.

You could do something like this as a compromise, but it's hackish:

1 rem
2 rem fizzbuzz in commodore basic
3 rem
10 t = time
20 for i = 1 to 100
30 n = i / 15 :if n = int(n) then print "fizzbuzz" :next
40 n = i / 3 :if n = int(n) then print "fizz" :next
50 n = i / 5 :if n = int(n) then print "buzz" :next :goto 70
60 print i: next
70 print "ran for" (time - t) / 60 "seconds"

Edward had a good idea: keep separate counters for fizz and buzz instead of doing division. Here's a variation on his approach that sacrifices speed for readability. It doesn't use goto and is about as readable as the original code in the question. It's faster than my examples above, but slower than Edward's solution (since it doesn't skip other conditions after passing one, and doesn't have a combined "fizzbuzz" case).

1 rem
2 rem fizzbuzz in commodore basic
3 rem
10 t = time
20 fizz = 0 :buzz = 0
30 for i = 1 to 100
40 fizz = fizz + 1 :buzz = buzz + 1
50 print
60 if fizz = 3 then print "fizz"; :fizz = 0
70 if buzz = 5 then print "buzz"; :buzz = 0
80 if fizz > 0 and buzz > 0 then print i;
90 next
100 print ,"ran for" (time - t) / 60 "seconds"

Incidentally, the biggest performance bottleneck seems to be printing carriage returns and moving everything up the screen. If you try the original code in the question without printing any carriage returns, it will run about three times faster.

Assuming the screen has 40 columns by 25 rows, it's just big enough to fit the fizzbuzz output neatly into columns without scrolling. This version does just that, shaving off about a second from the previous version.

1 rem
2 rem fizzbuzz in commodore basic
3 rem
10 t = time
15 print chr$(147);
20 fizz = 0 :buzz = 0 :row = 0 :col = 0
30 for i = 1 to 100
40 fizz = fizz + 1 :buzz = buzz + 1
50 row = row + 1
60 poke 211, col
70 if fizz = 3 then print "fizz"; :fizz = 0
80 if buzz = 5 then print "buzz"; :buzz = 0
90 if fizz > 0 and buzz > 0 then print i;
100 if row = 25 then print chr$(19); :col = col + 10 :row = 0
110 if row > 0 then print
120 next
130 secs = (time - t) / 60
140 poke 198, 0 :wait 198, 1 
150 print chr$(147) "ran for" secs "seconds"

Notes:

  • chr$(147) is CLR, a form feed character which clears the screen.

  • poke 211, col manipulates system memory at the address where the cursor column is stored.

  • chr$(19) is HOME; it moves the cursor to the top row.

  • poke 198, 0 :wait 198, 1 waits for the user to press a key before continuing, so the timing message and READY prompt don't overwrite any of the output at the end.

Here's what the output from the final version looks like:

C64 fizzbuzz output

\$\endgroup\$
1
  • \$\begingroup\$ I left the program in lowercase because when I paste uppercase letters into VICE I get gibberish. \$\endgroup\$
    – Dagg
    Commented Jul 13, 2014 at 8:48
12
\$\begingroup\$

Why use division at all? It's faster if you keep counters instead. This program keeps two counters, H for counting by 3 and V for counting by 5. This runs in under 5 seconds in Vice (PET 2001 mode) and also 5 seconds on my actual PET, which I've owned since 1977.

1 REM
2 REM FIZZBUZZ IN COMMODORE BASIC
3 REM
10 T = TI
20 H = 3
30 V = 5
40 FOR I = 1 TO 100
50 H = H-1
60 V = V-1
70 IF H <> 0 THEN 130
80 H = 3
90 IF V = 0 THEN 170
100 PRINT "FIZZ"
110 NEXT
120 GOTO 220
130 IF V = 0 THEN 190
140 PRINT I
150 NEXT
160 GOTO 220
170 PRINT "FIZZBUZZ"
180 GOTO 200
190 PRINT "BUZZ"
200 V = 5
210 NEXT
220 PRINT (TI-T)/60
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.