## [Code question] variables and unwanted results.

General FreeBASIC programming questions.
Krieger
Posts: 4
Joined: Nov 09, 2018 11:16

### [Code question] variables and unwanted results.

Greetings,

I have not used Freebasic for roughly 8 years and recently picked it up again. To my understanding (which could be horribly wrong) the compiler should be able to handle self referencing variables as in: x = (x +1) , x = (x +y) / x.

I wanted to make a doodle ( code sketch for fun) around a very simple CPU based 3D pixel plotter but all my attempts failed horribly each time I wanted to expand it beyond the simplest of basics. After a long search and 3 new versions of the code i came to the conclusion that variables or arrays which reference their own self while altering their own self cause them to contain completely different data than intended.

The code bellow was written to test it on my machine. Every Sub generate a different result on my machine:

Code: Select all

`#define D_PI            3.141592654##define D_PI180            (D_PI / 2)#define D_TABLESIZE        359dim shared as single costable(D_TABLESIZE)dim shared as single sintable(D_TABLESIZE)dim as integer anglemodfor i as integer = 0 to 359   sintable(i) = sin( i * D_PI180)   costable(i) = cos( i * D_PI180)next isub TheXA (x as integer, y as integer, z as integer, xangle as integer, yangle as integer, zangle as integer)   DIM AS INTEGER NewX, NewY, NewZ   NewY = y * cos(XAngle * (D_PI / 180)) - z * sin(XAngle * (D_PI / 180))   NewZ = z * cos(XAngle * (D_PI / 180)) + Y * sin(XAngle * (D_PI / 180))      locate 4,1   print "newY : "; NewY   print "NewZ : "; NewZend subsub TheXB (x as integer, y as integer, z as integer, xangle as integer, yangle as integer, zangle as integer)   y = y * costable(xangle) - z * sintable(xangle)   z = z * costable(xangle) + y * sintable(xangle)         locate 8,1   print "TestY : "; y   print "TestZ : "; zend subsub TheXC (x as integer, y as integer, z as integer, xangle as integer, yangle as integer, zangle as integer)   y = y * cos(XAngle * (D_PI / 180)) - z * sin(XAngle * (D_PI / 180))   z = z * cos(XAngle * (D_PI / 180)) + y * sin(XAngle * (D_PI / 180))   locate 12,1   print "TestY2 : "; y   print "TestZ2 : "; zend subscreenres (800,600, 32)do while not multikey(&h01)anglemod = (anglemod +1) mod 360   TheXA (50, 50, 50, anglemod, anglemod, anglemod)   TheXB (50, 50, 50, anglemod, anglemod, anglemod)   TheXC (50, 50, 50, anglemod, anglemod, anglemod)      sleep 100      loop`

i use Freebasic-1.05.0-x86_64 on debian stretch
Last edited by Krieger on Nov 10, 2018 1:14, edited 2 times in total.
counting_pine
Posts: 6170
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

### Re: [Code question] Self referencing variables: Human error or bug ?

Hi Krieger, welcome to the forum!

Subs/Functions can modify parameters, if they are passed by reference ('byref'), rather than by value ('byval').
In FreeBASIC, the default used to be that all parameters where passed byref by default, but at some point the default changed so that scalars (integer/floating point/pointer types) are passed byval for speed/predictability.

You can choose which way they are passed by putting 'byref'/'byval' before the parameter name:

Code: Select all

`sub TheXA (byref x as integer, byref y as integer, byref z as integer, _           byval xangle as integer, byval yangle as integer, byval zangle as integer)`

By the way, you may need to be careful with this kind of calculation:

Code: Select all

`y = y * cs - z * snz = z * cs + y * sn`
Because the value of y will change before the new value of z is calculated. The best way to avoid this kind of confusion is to store the previous values of y/z in temporary variables, and use those for calculating the new values.
dodicat
Posts: 5886
Joined: Jan 10, 2006 20:30
Location: Scotland

### Re: [Code question] Self referencing variables: Human error or bug ?

You have
#define D_PI180 (D_PI / 2)
but shouldn't it be
#define D_PI180 (D_PI /180)
In the subs 2 and 3 you have the value of y changed in the first line, then you use this new value for the second line.
Sub one looks good, clean values.
Krieger
Posts: 4
Joined: Nov 09, 2018 11:16

### Re: [Code question] Self referencing variables: Human error or bug ?

counting_pine wrote:Hi Krieger, welcome to the forum!

By the way, you may need to be careful with this kind of calculation:

Code: Select all

`y = y * cs - z * snz = z * cs + y * sn`

Because the value of y will change before the new value of z is calculated. The best way to avoid this kind of confusion is to store the previous values of y/z in temporary variables, and use those for calculating the new values.

dodicat wrote:You have
#define D_PI180 (D_PI / 2)
but shouldn't it be
#define D_PI180 (D_PI /180)
In the subs 2 and 3 you have the value of y changed in the first line, then you use this new value for the second line.
Sub one looks good, clean values.

Thank you for taking the time to read the code. Though the results still remain a riddle to me, even after i changed the code to be certain that the variables remain clean. I tried 3 different versions of the code, and each time it reacts differently. I am not new to programming at all so this kind of bothers me a bit.

What i also notice is how drastically the results change whenever i use a #define to hold the value of (D_PI / 180) as opposed to using the (D_PI / 180) directly inside of the subs. The values go haywire when using a define for that.

Is this truly a human mistake, or is this something the compiler does and of which i miss the point ? something along the lines of typecasting, or similar.

The new code, note that this, including the previous code block only contained a test for the problem:

Code: Select all

`#define D_PI            3.141592654##define D_TABLESIZE        359dim shared as single costable(D_TABLESIZE)dim shared as single sintable(D_TABLESIZE)dim as integer anglemodfor i as integer = 0 to 359   sintable(i) = sin( i * (D_PI / 180))   costable(i) = cos( i * (D_PI / 180))next isub TheXA (x as integer, y as integer, z as integer, xangle as integer, yangle as integer, zangle as integer)   DIM AS INTEGER NewX, NewY, NewZ   NewY = y * cos(XAngle * (D_PI / 180)) - z * sin(XAngle * (D_PI / 180))   NewZ = z * cos(XAngle * (D_PI / 180)) + Y * sin(XAngle * (D_PI / 180))      locate 4,1   print "newY : "; NewY   print "NewZ : "; NewZend subsub TheXB (x as integer, y as integer, z as integer, xangle as integer, yangle as integer, zangle as integer)      DIM AS INTEGER NewY, NewZ   NewY = y * costable(xangle) - z * sintable(xangle)   NewZ = z * costable(xangle) + y * sintable(xangle)         locate 8,1   print "TestY : "; NewY   print "TestZ : "; NewZend subsub TheXC (x as integer, y as integer, z as integer, xangle as integer, yangle as integer, zangle as integer)   DIM AS INTEGER NewY, NewZ   NewY = y * cos(XAngle * (D_PI / 180)) - z * sin(XAngle * (D_PI / 180))   NewZ = z * cos(XAngle * (D_PI / 180)) + y * sin(XAngle * (D_PI / 180))   locate 12,1   print "TestY2 : "; y   print "TestZ2 : "; zend subscreenres (800,600, 32)do while not multikey(&h01)anglemod = (anglemod +1) mod 360   TheXA (50, 50, 50, anglemod, anglemod, anglemod)   TheXB (50, 50, 50, anglemod, anglemod, anglemod)   TheXC (50, 50, 50, anglemod, anglemod, anglemod)      sleep 100      loop`

The results i get are:

Please ignore the blue line in case your wondering if i posted the wrong code.That blue line on the left is a window border of a window manager which i am using at the time.
MrSwiss
Posts: 3189
Joined: Jun 02, 2013 9:27
Location: Switzerland

### Re: [Code question] variables and unwanted results.

You might want to use constants (instead of #define), along below example:

Code: Select all

`Const As Double PI = 4 * Atn(1.0), d2r = PI / 180.0, r2d = 180.0 / PI#Define RAD(d)  ( (d) * d2r )  ' conversion single line macro: DEG --> RAD#Define DEG(r)  ( (r) * r2d )  ' conversion single line macro: RAD --> DEGFor i As ULong = 0 To 360  ' (0 To 359) isn't closing the circle!    Print "DEG: "; DEG(RAD(i)); Tab(14); "RAD: "; RAD(i)Next' DEG(RAD(i)) = i; just made so, to proof the proper working!Sleep`
Krieger
Posts: 4
Joined: Nov 09, 2018 11:16

### Re: [Code question] variables and unwanted results.

Interesting. A macro which addresses a constant produces the desired results, while a define would generate some kind of problem. Yet again, thank you. This reinvigorates me to completely redo what i was doing from scratch. I was almost ready to go back to C, although i love FB and how optimized and advanced it is for being a basic language.
fxm
Posts: 9070
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

### Re: [Code question] variables and unwanted results.

@Krieger,
Can you explain where is the problem in your last code above?

'TheXC()' procedure only shows that the passed parameter for example 'y' is not modified by the procedure body expressions because it is never reassigned (no expression with 'y = .....' in that case).
Last edited by fxm on Nov 10, 2018 8:57, edited 1 time in total.
Haubitze
Posts: 37
Joined: May 20, 2016 8:42

### Re: [Code question] variables and unwanted results.

how fxm says, in your last code example in the XC sub you have to print out NewZ/Y not x/y ;)

Code: Select all

`sub TheXC (x as integer, y as integer, z as integer, xangle as integer, yangle as integer, zangle as integer)   DIM AS INTEGER NewY, NewZ   NewY = y * cos(XAngle * (D_PI / 180)) - z * sin(XAngle * (D_PI / 180))   NewZ = z * cos(XAngle * (D_PI / 180)) + y * sin(XAngle * (D_PI / 180))   locate 12,1   print "TestY2 : "; NewY   print "TestZ2 : "; NewZend sub`

then i think it is all the same output, but im not tested.

salute
fxm
Posts: 9070
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

### Re: [Code question] variables and unwanted results.

Krieger wrote:A macro which addresses a constant produces the desired results, while a define would generate some kind of problem.

No if you put surrounding parentheses to avoid unwanted precedence change of operators:

Code: Select all

`#Define PI    ( 4 * Atn(1.0) )#Define d2r   ( PI / 180.0 )#Define r2d   ( 180.0 / PI )#Define RAD(d)  ( (d) * d2r )  ' conversion single line macro: DEG --> RAD#Define DEG(r)  ( (r) * r2d )  ' conversion single line macro: RAD --> DEGFor i As ULong = 0 To 360  ' (0 To 359) isn't closing the circle!    Print "DEG: "; DEG(RAD(i)); Tab(14); "RAD: "; RAD(i)Next' DEG(RAD(i)) = i; just made so, to proof the proper working!Sleep`
Krieger
Posts: 4
Joined: Nov 09, 2018 11:16

### Re: [Code question] variables and unwanted results.

Thank you all for the time you took to look at the code. I realize now that i was making some mistakes too, it were long nights and i was staring at the screen for way too long. I indeed made some mistakes concerning the parenthesis, but i also ran a lot of newly made time consuming tests to find some issues with my actual code (not the example code), so by now i am making progress with the original program.

I still have some concerns in terms of the correct output based upon the tests i did, but i do hope them to be irrelevant and the product of me not being fully awake at the time.
dodicat
Posts: 5886
Joined: Jan 10, 2006 20:30
Location: Scotland

### Re: [Code question] variables and unwanted results.

Remember that as a test you can compile your code with the -pp compiler switch.
You will get another .bas file (something.pp.bas) , you will see all your defines and macros inserted into your code.