Division by 0 error in my program
Root / Programming Questions / [.]
mikl_plsCreated:
Hey everyone. This is my first post here.
I'm trying to recreate a "3D starfield" program I downloaded for QBASIC a long time ago to SmileBASIC and am having some issues with doing so. I would greatly appreciate some help if anyone is able to lend some advice.
DIM STARAMT, X, DEPTH, ZOOM PRINT "SIMPLE STARFIELD SIMULATOR" PRINT "--------------------------" PRINT PRINT "By the RAZE, Feb 2000" PRINT "Converted to SmileBASIC by Michael Wood 5/6/2019" PRINT PRINT "Press any button to continue." REPEAT UNTIL BUTTON(3) CLS INPUT " Amount of stars (default: 30): ", STARAMT INPUT " Depth (default: 40): ", DEPTH INPUT " Zoom (default: 200): ", ZOOM IF STARAMT==0 THEN STARAMT = 30 IF DEPTH==0 THEN DEPTH = 40 IF ZOOM==0 THEN ZOOM = 200 DIM STARPOS[STARAMT] DIM STARX[STARAMT], STARY[STARAMT], STARZ[STARAMT] FOR X = 0 TO STARAMT - 1 GOSUB @DEFPOS NEXT COL = 0 @LOOP FOR X = 0 TO STARAMT-1 X3D = STARX[X] Y3D = STARY[X] Z3D = STARZ[X] COL = 255 GOSUB @XYZLOC NEXT VSYNC 1 FOR X = 0 TO STARAMT-1 GCLS STARZ[X] = STARZ[X] - 1 IF STARPOS[X]==1 THEN GOSOUB @DEFPOS: STARPOS[X] = 0 NEXT GOTO @LOOP @DEFPOS STARX[X] = RND(30) STARY[X] = RND(30) IF STARX[X] == 0 THEN STARX[X] = 1 IF STARY[X] == 0 THEN STARY[X] = 1 'OTHERWISE STAR COMES STRAIGHT AHEAD AND CAUSES AN ERROR IF STARPOS[X] <= 0 THEN STARZ[X] = RND(50)+1 ELSEIF STARPOS[X] != 0 THEN STARZ[X] = 50 ENDIF RETURN @XYZLOC 'BELOW ARE THE TWO ESSENTIAL 3D PLOTTING LINES 'FINDS THE X LOCATION ON SCREEN X2D = ZOOM * (X3D / (Z3D + DEPTH)) + 200 Y2D = ZOOM * (Y3D / (Z3D + DEPTH)) + 120 IF X2D < 0 OR X2D > 399 OR Y2D < 0 OR Y2D > 239 THEN STARPOS[X] = 1 RETURN ENDIF IF COL == 0 THEN GPSET X2D, Y2D, RGB(COL,COL,COL) RETURN ENDIF C = 255 * Z3D COL = SQR(C) 'PROBLEM LINE IF Z3D > 0 THEN GPSET X2D,Y2D,RGB(COL,COL,COL) RETURN ENDIF GPSET X2D,Y2D,RGB(COL,COL,COL) RETURN
Z3D can be a negative number, and Square Root of a negative number is undefined in SmileBasic. You also have a divide by zero error if Z3D + DEPTH == 0. There is redundant pixel plotting in @XYZLOC, and you should be using functions and not label loops/gosub as well. I would also kick the default star amount up to 300, SmileBasic can handle it and it looks a lot better. Basically, I think your code should look like this:
DIM STARAMT, X, DEPTH, ZOOM PRINT "SIMPLE STARFIELD SIMULATOR" PRINT "--------------------------" PRINT PRINT "By the RAZE, Feb 2000" PRINT "Converted to SmileBASIC by Michael Wood 5/6/2019" PRINT PRINT "Press any button to continue." REPEAT VSYNC 'Don't waste battery life, add a VSYNC UNTIL BUTTON(3) CLS INPUT " Amount of stars (default: 300): ", STARAMT INPUT " Depth (default: 40): ", DEPTH INPUT " Zoom (default: 200): ", ZOOM CLS 'Another CLS so the input text doesn't linger on screen. IF STARAMT==0 THEN STARAMT = 300 IF DEPTH==0 THEN DEPTH = 40 IF ZOOM==0 THEN ZOOM = 200 DIM STARPOS[STARAMT] DIM STARX[STARAMT], STARY[STARAMT], STARZ[STARAMT] 'I don't like that we are using X as an index counter, but I am going to leave it as-is. To me x should be for a x,y location by default. FOR X = 0 TO STARAMT - 1 DEFPOS X NEXT X WHILE TRUE 'Using a while loop instead of a label loop. FOR X = 0 TO STARAMT-1 PLOTXYZ X, STARX[X], STARY[X], STARZ[X] NEXT X VSYNC 1 GCLS 'Moved this out of the FOR loop it was causing only one star to appear and was a bug. FOR X = 0 TO STARAMT-1 STARZ[X] = STARZ[X] - 1 IF STARPOS[X]==1 THEN DEFPOS X STARPOS[X] = 0 ENDIF NEXT X IF (BUTTON(3) AND #B) != 0 THEN BREAK WEND DEF DEFPOS I REPEAT 'Repeat loop makes sure you aren't generating a 0,0 'This was only drawing in the bottom left quadrant we need X,Y to have both positive and negative values. I also increased the range a bit. STARX[X] = RND(100) - 50 STARY[X] = RND(100) - 50 UNTIL STARX[X] != 0 && STARY[X] != 0 '0, 0 IS DISALLOWED OTHERWISE STAR COMES STRAIGHT AHEAD AND CAUSES AN ERROR IF STARPOS[X] <= 0 THEN STARZ[X] = RND(50)+1 ELSEIF STARPOS[X] != 0 THEN 'The STARPOS[X] != 0 Doesn't really do anything, left it to keep the code looking the same STARZ[X] = 50 ENDIF END 'X, Y, and Z are local variables in a function. I don't like mutating globals without a reason. This lets me use shorter names too. DEF PLOTXYZ I, X, Y, Z VAR SCREEN_W = 400, SCREEN_H = 240 VAR HALF_W = SCREEN_W / 2 VAR HALF_H = SCREEN_H / 2 VAR X2D, Y2D 'No longer global variables 'BELOW ARE THE TWO ESSENTIAL 3D PLOTTING LINES 'FINDS THE X LOCATION ON SCREEN IF Z + DEPTH != 0 THEN X2D = ZOOM * (X / (Z + DEPTH)) + HALF_W 'Using variables with descriptive names to explain what the code is doing. Magic numbers are bad Y2D = ZOOM * (Y / (Z + DEPTH)) + HALF_H ELSE X2D = 0 'Here I am using magic numbers right after saying they are bad. I figure 0 is a good replacement for a divide by zero error Y2D = 0 ENDIF IF X2D < 0 OR X2D >= SCREEN_W OR Y2D < 0 OR Y2D >= SCREEN_H THEN STARPOS[I] = 1 RETURN ENDIF 'SQUARE ROOT OF A NEGATIVE NUMBER THROWS 'AN ERROR IF Z > 0 THEN COL = SQR(255 * Z) ELSE COL = 255 'WHITE ENDIF GPSET X2D, Y2D, RGB(COL,COL,COL) ENDI tried to comment my changes, but feel free to ask any questions you may have. I didn't copy it over with petite modem so let me know of any omissions or type-os too.
Just make sure C is not negative, C=MAX(255*Z3D,0)Thank you!
What line number or function is stated in the error message? That would help more.It's line #76.
Z3D can be a negative number, and Square Root of a negative number is undefined in SmileBasic. You also have a divide by zero error if Z3D + DEPTH == 0. There is redundant pixel plotting in @XYZLOC, and you should be using functions and not label loops/gosub as well. I would also kick the default star amount up to 300, SmileBasic can handle it and it looks a lot better. Basically, I think your code should look like this:OMG THANK YOU SO MUCH!! I'm very new to SmileBASIC, so all of y'all's help is MUCH APPRECIATED!! :DDIM STARAMT, X, DEPTH, ZOOM PRINT "SIMPLE STARFIELD SIMULATOR" PRINT "--------------------------" PRINT PRINT "By the RAZE, Feb 2000" PRINT "Converted to SmileBASIC by Michael Wood 5/6/2019" PRINT PRINT "Press any button to continue." REPEAT VSYNC 'Don't waste battery life, add a VSYNC UNTIL BUTTON(3) CLS INPUT " Amount of stars (default: 300): ", STARAMT INPUT " Depth (default: 40): ", DEPTH INPUT " Zoom (default: 200): ", ZOOM CLS 'Another CLS so the input text doesn't linger on screen. IF STARAMT==0 THEN STARAMT = 300 IF DEPTH==0 THEN DEPTH = 40 IF ZOOM==0 THEN ZOOM = 200 DIM STARPOS[STARAMT] DIM STARX[STARAMT], STARY[STARAMT], STARZ[STARAMT] 'I don't like that we are using X as an index counter, but I am going to leave it as-is. To me x should be for a x,y location by default. FOR X = 0 TO STARAMT - 1 DEFPOS X NEXT X WHILE TRUE 'Using a while loop instead of a label loop. FOR X = 0 TO STARAMT-1 PLOTXYZ X, STARX[X], STARY[X], STARZ[X] NEXT X VSYNC 1 GCLS 'Moved this out of the FOR loop it was causing only one star to appear and was a bug. FOR X = 0 TO STARAMT-1 STARZ[X] = STARZ[X] - 1 IF STARPOS[X]==1 THEN DEFPOS X STARPOS[X] = 0 ENDIF NEXT X IF (BUTTON(3) AND #B) != 0 THEN BREAK WEND DEF DEFPOS I REPEAT 'Repeat loop makes sure you aren't generating a 0,0 'This was only drawing in the bottom left quadrant we need X,Y to have both positive and negative values. I also increased the range a bit. STARX[X] = RND(100) - 50 STARY[X] = RND(100) - 50 UNTIL STARX[X] != 0 && STARY[X] != 0 '0, 0 IS DISALLOWED OTHERWISE STAR COMES STRAIGHT AHEAD AND CAUSES AN ERROR IF STARPOS[X] <= 0 THEN STARZ[X] = RND(50)+1 ELSEIF STARPOS[X] != 0 THEN 'The STARPOS[X] != 0 Doesn't really do anything, left it to keep the code looking the same STARZ[X] = 50 ENDIF END 'X, Y, and Z are local variables in a function. I don't like mutating globals without a reason. This lets me use shorter names too. DEF PLOTXYZ I, X, Y, Z VAR SCREEN_W = 400, SCREEN_H = 240 VAR HALF_W = SCREEN_W / 2 VAR HALF_H = SCREEN_H / 2 VAR X2D, Y2D 'No longer global variables 'BELOW ARE THE TWO ESSENTIAL 3D PLOTTING LINES 'FINDS THE X LOCATION ON SCREEN IF Z + DEPTH != 0 THEN X2D = ZOOM * (X / (Z + DEPTH)) + HALF_W 'Using variables with descriptive names to explain what the code is doing. Magic numbers are bad Y2D = ZOOM * (Y / (Z + DEPTH)) + HALF_H ELSE X2D = 0 'Here I am using magic numbers right after saying they are bad. I figure 0 is a good replacement for a divide by zero error Y2D = 0 ENDIF IF X2D < 0 OR X2D >= SCREEN_W OR Y2D < 0 OR Y2D >= SCREEN_H THEN STARPOS[I] = 1 RETURN ENDIF 'SQUARE ROOT OF A NEGATIVE NUMBER THROWS 'AN ERROR IF Z > 0 THEN COL = SQR(255 * Z) ELSE COL = 255 'WHITE ENDIF GPSET X2D, Y2D, RGB(COL,COL,COL) ENDI tried to comment my changes, but feel free to ask any questions you may have. I didn't copy it over with petite modem so let me know of any omissions or type-os too.
Well, I tried all the recommendations, and it still doesn't work. I had to fix a few things (like using x as an index counter) for the program to even work. Now I get subscript out of range errors even though it says "STARAMT - 1" (still going to 300). I think I'm going to call this program a lost cause lol.
I REALLY appreciate everyone's help and contributions to this!
I uploaded my version of the code, here is the key: KY4D3N3. Let me know when you have it so I can reuse the key space.