[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.

Postby Krieger » Nov 09, 2018 11:29

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        359

dim shared as single costable(D_TABLESIZE)
dim shared as single sintable(D_TABLESIZE)
dim as integer anglemod

for i as integer = 0 to 359
   sintable(i) = sin( i * D_PI180)
   costable(i) = cos( i * D_PI180)
next i


sub 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 : "; NewZ
end sub

sub 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 : "; z
end sub

sub 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 : "; z
end sub

screenres (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
Site Admin
Posts: 5988
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

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

Postby counting_pine » Nov 09, 2018 13:22

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 * sn
z = 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: 5228
Joined: Jan 10, 2006 20:30
Location: Scotland

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

Postby dodicat » Nov 09, 2018 13:39

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 ?

Postby Krieger » Nov 10, 2018 1:09

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 * sn
z = 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        359

dim shared as single costable(D_TABLESIZE)
dim shared as single sintable(D_TABLESIZE)
dim as integer anglemod

for i as integer = 0 to 359
   sintable(i) = sin( i * (D_PI / 180))
   costable(i) = cos( i * (D_PI / 180))
next i


sub 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 : "; NewZ
end sub

sub 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 : "; NewZ
end sub

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 : "; y
   print "TestZ2 : "; z
end sub

screenres (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:
Image
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: 2811
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: [Code question] variables and unwanted results.

Postby MrSwiss » Nov 10, 2018 1:45

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 --> DEG


For 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.

Postby Krieger » Nov 10, 2018 1:56

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: 8424
Joined: Apr 22, 2009 12:46
Location: Paris (suburbs), FRANCE

Re: [Code question] variables and unwanted results.

Postby fxm » Nov 10, 2018 6:34

@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: 20
Joined: May 20, 2016 8:42

Re: [Code question] variables and unwanted results.

Postby Haubitze » Nov 10, 2018 8:45

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 : "; NewZ
end sub


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

salute
fxm
Posts: 8424
Joined: Apr 22, 2009 12:46
Location: Paris (suburbs), FRANCE

Re: [Code question] variables and unwanted results.

Postby fxm » Nov 10, 2018 9:18

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 --> DEG


For 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.

Postby Krieger » Nov 10, 2018 10:39

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: 5228
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: [Code question] variables and unwanted results.

Postby dodicat » Nov 10, 2018 10:57

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.

Return to “General”

Who is online

Users browsing this forum: jj2007 and 3 guests