[Code question] variables and unwanted results.

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

[Code question] variables and unwanted results.

Post by Krieger »

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: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

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

Post by counting_pine »

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

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

Post by dodicat »

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 ?

Post by Krieger »

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: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: [Code question] variables and unwanted results.

Post by MrSwiss »

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.

Post by Krieger »

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
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: [Code question] variables and unwanted results.

Post by fxm »

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

Re: [Code question] variables and unwanted results.

Post by Haubitze »

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
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: [Code question] variables and unwanted results.

Post by fxm »

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.

Post by Krieger »

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

Re: [Code question] variables and unwanted results.

Post by dodicat »

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.
Post Reply