You are not logged in.

1

Thursday, February 25th 2010, 9:09am

POSSIBLE BUG with if-statement comparison operators when used with negative numbers?

I'm having a problem performing comparisons when the arguments are negative numbers. A simple example:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
<krpano version="1.0.8" onstart="lt_simple_test" >

	<action name="lt_simple_test">
		if (-270 LT -180,
			trace("correct");
		<!--else-->,
			js(alert("INCORRECT RESULT: (-270 LT -180) evaluates to false"));
		);
	</action>

	<preview type="grid(cube,16,16,512,0xCCCCCC,0xFFFFFF,0x999999);" details="16" />
</krpano>


This code will show that the (-270 LT -180) evaluates as false for release 1.0.8 beta 9 (build 2010-02-21). I was definitely seeing problems with the previous release as well. By the way, this particular set of numbers also produces the incorrect result for LE, GT and GE.

Not all negative number combinations produce errors however, (-300 LT -30) is wrong but (-300 LT -60) is correct. (-60 LT -90) is incorrect but (-60 LT -120) is right. One thing that is consistent, is that whenever the error occurs , both numbers will be negative.

Here is some code to test the operator across a range of numbers. It sweeps each operand from -360 to 360 in increments of 30:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<krpano version="1.0.8" onstart="loop_test();js(alert('please press the o key to see error listing'));" >

	<!-- loop_test
		runs the lt_test comparison test function with arguments a and b across a range of numbers -->
	<action name="loop_test">
		set(a,-360);
		while(a LE 360,
			set(b,-360);
			while(b LE 360,
				lt_test(get(a),get(b));
				inc(b,30);
			);
			inc(a,30);
			wait(0);
		);
	</action>

	<!-- lt_test <a.num> <b.num>
		test function to check correctness of LT operator by comparing it with a comparison computed in an alternate way -->
	<action name="lt_test"> <!-- a, b -->
		if (%1 LT %2,set(_lt,true);,set(_lt,false);); <!-- do the comparison, save the result in _lt -->

		<!-- because the error seems to occur only when comparing with a negative number with a negative number, subtract and compare with zero-->
		sub(_check,%1,%2);
		if (_check LT 0,set(_ck,true);,set(_ck,false););
		if (_lt != _ck,
			trace("error: LT evaluates (%1 LT %2)=",get(_lt));
		);
	</action>

	<!-- _while_action
		private function to execute while loop action -->
	<action name="_while_action">
		trace("Error: content popped too many times in while loop");
	</action>
	<!-- while <condition.str> <action.str>
		while loop function which repeatedly iterates the action as long as the condition is true-->
	<action name="while"> <!--condition.str,action.str-->
		push(action[_while_action].content);
		set(action[_while_action].content,"if(%1,%2;_while_action(););");
		_while_action();
		pop(action[_while_action].content);
	</action>

	<preview type="grid(cube,16,16,512,0xCCCCCC,0xFFFFFF,0x999999);" details="16" />

</krpano>


This produces the following result. Note that not all comparisons produce erroneous results:

Quoted

INFO: krpano 1.0.8 beta 9 (build 2010-02-21)
INFO: Flashplayer WIN 10,0,2,54 ActiveX (debug)
INFO: registered to: pinsane
INFO: error: LT evaluates (-360 LT -330)=false
INFO: error: LT evaluates (-360 LT -300)=false
INFO: error: LT evaluates (-360 LT -270)=false
INFO: error: LT evaluates (-360 LT -240)=false
INFO: error: LT evaluates (-360 LT -210)=false
INFO: error: LT evaluates (-360 LT -180)=false
INFO: error: LT evaluates (-360 LT -150)=false
INFO: error: LT evaluates (-360 LT -120)=false
INFO: error: LT evaluates (-360 LT -30)=false
INFO: error: LT evaluates (-330 LT -360)=true
INFO: error: LT evaluates (-330 LT -300)=false
INFO: error: LT evaluates (-330 LT -270)=false
INFO: error: LT evaluates (-330 LT -240)=false
INFO: error: LT evaluates (-330 LT -210)=false
INFO: error: LT evaluates (-330 LT -180)=false
INFO: error: LT evaluates (-330 LT -150)=false
INFO: error: LT evaluates (-330 LT -120)=false
INFO: error: LT evaluates (-330 LT -30)=false
INFO: error: LT evaluates (-300 LT -360)=true
INFO: error: LT evaluates (-300 LT -330)=true
INFO: error: LT evaluates (-300 LT -270)=false
INFO: error: LT evaluates (-300 LT -240)=false
INFO: error: LT evaluates (-300 LT -210)=false
INFO: error: LT evaluates (-300 LT -180)=false
INFO: error: LT evaluates (-300 LT -150)=false
INFO: error: LT evaluates (-300 LT -120)=false
INFO: error: LT evaluates (-300 LT -30)=false
INFO: error: LT evaluates (-270 LT -360)=true
INFO: error: LT evaluates (-270 LT -330)=true
INFO: error: LT evaluates (-270 LT -300)=true
INFO: error: LT evaluates (-270 LT -240)=false
INFO: error: LT evaluates (-270 LT -210)=false
INFO: error: LT evaluates (-270 LT -180)=false
INFO: error: LT evaluates (-270 LT -150)=false
INFO: error: LT evaluates (-270 LT -120)=false
INFO: error: LT evaluates (-240 LT -360)=true
INFO: error: LT evaluates (-240 LT -330)=true
INFO: error: LT evaluates (-240 LT -300)=true
INFO: error: LT evaluates (-240 LT -270)=true
INFO: error: LT evaluates (-240 LT -210)=false
INFO: error: LT evaluates (-240 LT -180)=false
INFO: error: LT evaluates (-240 LT -150)=false
INFO: error: LT evaluates (-240 LT -120)=false
INFO: error: LT evaluates (-210 LT -360)=true
INFO: error: LT evaluates (-210 LT -330)=true
INFO: error: LT evaluates (-210 LT -300)=true
INFO: error: LT evaluates (-210 LT -270)=true
INFO: error: LT evaluates (-210 LT -240)=true
INFO: error: LT evaluates (-210 LT -180)=false
INFO: error: LT evaluates (-210 LT -150)=false
INFO: error: LT evaluates (-210 LT -120)=false
INFO: error: LT evaluates (-180 LT -360)=true
INFO: error: LT evaluates (-180 LT -330)=true
INFO: error: LT evaluates (-180 LT -300)=true
INFO: error: LT evaluates (-180 LT -270)=true
INFO: error: LT evaluates (-180 LT -240)=true
INFO: error: LT evaluates (-180 LT -210)=true
INFO: error: LT evaluates (-180 LT -150)=false
INFO: error: LT evaluates (-180 LT -120)=false
INFO: error: LT evaluates (-150 LT -360)=true
INFO: error: LT evaluates (-150 LT -330)=true
INFO: error: LT evaluates (-150 LT -300)=true
INFO: error: LT evaluates (-150 LT -270)=true
INFO: error: LT evaluates (-150 LT -240)=true
INFO: error: LT evaluates (-150 LT -210)=true
INFO: error: LT evaluates (-150 LT -180)=true
INFO: error: LT evaluates (-150 LT -120)=false
INFO: error: LT evaluates (-120 LT -360)=true
INFO: error: LT evaluates (-120 LT -330)=true
INFO: error: LT evaluates (-120 LT -300)=true
INFO: error: LT evaluates (-120 LT -270)=true
INFO: error: LT evaluates (-120 LT -240)=true
INFO: error: LT evaluates (-120 LT -210)=true
INFO: error: LT evaluates (-120 LT -180)=true
INFO: error: LT evaluates (-120 LT -150)=true
INFO: error: LT evaluates (-90 LT -60)=false
INFO: error: LT evaluates (-90 LT -30)=false
INFO: error: LT evaluates (-60 LT -90)=true
INFO: error: LT evaluates (-60 LT -30)=false
INFO: error: LT evaluates (-30 LT -360)=true
INFO: error: LT evaluates (-30 LT -330)=true
INFO: error: LT evaluates (-30 LT -300)=true
INFO: error: LT evaluates (-30 LT -90)=true
INFO: error: LT evaluates (-30 LT -60)=true


This appears to be a bug. Am I missing something?

steve

This post has been edited 1 times, last edit by "pinsane" (Feb 26th 2010, 1:59am)


2

Monday, March 1st 2010, 11:53am

Hi,

it's a bug, the negative values were interpreted as "Strings",
I have fixed that now for the next release,

btw - your "while" action is great!

best regards,
Klaus

3

Monday, March 1st 2010, 5:51pm

Quoted

it's a bug, the negative values were interpreted as "Strings",

Interesting. In the spirit of better understanding, could you elaborate as to how the if statement resolves it's predicate? I find it a little mysterious especially because I'm trying to write a robust isnull action. It's odd that only certain combinations of negative values seem to cause this to fail. It seems that the workaround is to subtract the values first and then compare with zero, but I must admit that automating such a check still relies on using the LT operator so I couldn't be sure if my test was completely valid.

Source code

1
I have fixed that now for the next release,

Thanks!

Quoted

btw - your "while" action is great!


Thanks. That's high praise indeed coming from you! I had considered making a post to share these looping actions (I've written actions for while, do, for, and foreach) but I've noticed that for big loops the recursion seems to be able to "get ahead" of the interpreter and abort due to an action overflow error. You'll note in the nested while loops in the above example, I put a wait(0) statement. If you remove this statement you'll see an overflow error for release 1.0.8 beta 9 (build 2010-02-21). The wait(0) seems to get around the problem, for reasons that are completely opaque to me (command queueing? garbage collection?), but I'd be interested if there is a more elegant, faster, and robust workaround. Also, I'll point out that I'm using push and pop so that the loops can be nested, but that means that if someone were to execute push/pop functions asymmetrically (# of pushes != # of pops) from within the loop then strange behavior would occur indeed. Personally, I think the best outcome would be to support looping directly from within the language ;-) because recursion is not the easiest thing for a neophyte programmer to implement.

Thanks for such a great program Klaus!

steve

4

Monday, March 8th 2010, 10:15am

Hi,
Interesting. In the spirit of better understanding, could you elaborate as to how the if statement resolves it's predicate? I find it a little mysterious especially because I'm trying to write a robust isnull action. It's odd that only certain combinations of negative values seem to cause this to fail. It seems that the workaround is to subtract the values first and then compare with zero, but I must admit that automating such a check still relies on using the LT operator so I couldn't be sure if my test was completely valid.
first - the krpano scripting is still a very simple one, please don't compare it with real scripting languages


the if action looks if there are variables with the give names:
if yes it takes the value of the variable for comparison,
if not it assumes the given variable is a string,

there is only one exception - when using the new '===' and '!==' if operators,
then 'null' will be used when a variable wasn't found,

then when the content of the variable or the given text itself is a string (note - all not predefined values are strings),
then the first character was checked if it was a numeric character, and if yes, it will be parsed and converted to a number,

btw. - there is a new version out where the negative values compare is fixed (now it checks also for a + or - sign before the number)

Thanks. That's high praise indeed coming from you! I had considered making a post to share these looping actions (I've written actions for while, do, for, and foreach) but I've noticed that for big loops the recursion seems to be able to "get ahead" of the interpreter and abort due to an action overflow error. You'll note in the nested while loops in the above example, I put a wait(0) statement. If you remove this statement you'll see an overflow error for release 1.0.8 beta 9 (build 2010-02-21). The wait(0) seems to get around the problem, for reasons that are completely opaque to me (command queueing? garbage collection?), but I'd be interested if there is a more elegant, faster, and robust workaround. Also, I'll point out that I'm using push and pop so that the loops can be nested, but that means that if someone were to execute push/pop functions asymmetrically (# of pushes != # of pops) from within the loop then strange behavior would occur indeed. Personally, I think the best outcome would be to support looping directly from within the language ;-) because recursion is not the easiest thing for a neophyte programmer to implement.
I have limited the number of single actions to be executed at once to 2000 at the moment,
it is still a simple interpreter and the flashplayer is not that fast in this case and so too much
actions will slow down the viewer too much,

and I have made this limit also to catch up user coding faults when writing "endless loops"
(the flashplayer could run and block the system up to 15 seconds when in an endloss loop!)

the wait(0) will work because it stops and current action queue, wait '0' () seconds and continue
later (on the next flashplayer timer/frame interval) again - btw. a great solution , maybe I should
do that automatically when reaching the limit of given actions (but then I had still the problem of
how to catch endless loops)

best regards,
Klaus

5

Wednesday, March 10th 2010, 9:36am

Quoted

btw. - there is a new version out where the negative values compare is fixed (now it checks also for a + or - sign before the number)


Yes!!!!! The new version works great! *thumbsup*

Quoted

...there is only one exception - when using the new '===' and '!==' if operators,
then 'null' will be used when a variable wasn't found,


Yessss again!!!!! I don't know how long these strict comparison operators have existed, but it seems that the considerable time I've spent refining an isnull action has been wasted. and that's a good thing!!!! *thumbup*

There is one area that still seems to be a problem with the strict operators. It's not obvious to me how to set a variable to null. Consider this example:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<krpano version="1.0.8" onstart="null_test" >
	<action name="null_test">
		if (foo === null,
			trace("yep, its null!");
		<!--else-->,
			js(alert('first test: ackkk! foo should be null'););
		);
		set(foo,null);
		if (foo === null,
			trace("yep, its null!");
		<!--else-->,
			js(alert('second test: ackkk! foo should be null'););
		);
	
	</action>
	<hotspot name="hs0"
	         url="%SWFPATH%../plugins/hotspot_ani_black.swf"
	         ath="0"
	         atv="5.14"
	         keep="false"
	         visible="true" enabled="true" handcursor="true" capture="true" children="true"
 	         zorder="0"
	         fillcolor     ="0xffffff" fillalpha     ="0.00" borderwidth     ="0.0" bordercolor     ="0xffffff" borderalpha     ="0.00"
	         fillcolorhover="0xffffff" fillalphahover="0.10" borderwidthhover="4.0" bordercolorhover="0xffffff" borderalphahover="0.80"
	         fadeintime="0.150" fadeouttime="0.300" fadeincurve="1.100" fadeoutcurve="0.700"
	         onover=""
	         onhover="showtext(Click here to go to StateB)"
	         onout=""
	         ondown=""
	         onclick="set(view.fovmin,5);looktohotspot(centerfront,10);state_machine_keep_begin(tour);loadpano(stateB.xml,null,MERGE,BLEND(2),state_machine_keep_end(tour);push(autorotate.enabled);set(autorotate.enabled,false);push(view.fovmin);set(view.fovmin,50.34);lookat(38.45,24.01,50.34);wait(blend);lookto(0,0,65,smooth(100,100,100));pop(view.fovmin);pop(autorotate.enabled))"
	         onup=""
		>
	</hotspot>
<preview type="grid(cube,16,16,512,0xCCCCCC,0xFFFFFF,0x999999);" details="16" />

</krpano>


Note that the second if statement fails. I'm assuming that it is because using set to assign null to the variable is turning it into a string, but then what is the proper way to set a variable to null?

And while we're talking about obscure corner cases, I ran into this one when creating this example. If you spell "it's" properly in the above example it fails, even though the single quote is between two double quotes. I'm at a loss to explain what's going on with this:

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<krpano version="1.0.8" onstart="null_test" >
	<action name="null_test">
		if (foo === null,
			trace("yep, it's null!");
		<!--else-->,
			js(alert('first test: ackkk! foo should be null'););
		);
		set(foo,null);
		if (foo === null,
			trace("yep, it's null!");
		<!--else-->,
			js(alert('second test: ackkk! foo should be null'););
		);
	
	</action>
	<hotspot name="hs0"
	         url="%SWFPATH%../plugins/hotspot_ani_black.swf"
	         ath="0"
	         atv="5.14"
	         keep="false"
	         visible="true" enabled="true" handcursor="true" capture="true" children="true"
 	         zorder="0"
	         fillcolor     ="0xffffff" fillalpha     ="0.00" borderwidth     ="0.0" bordercolor     ="0xffffff" borderalpha     ="0.00"
	         fillcolorhover="0xffffff" fillalphahover="0.10" borderwidthhover="4.0" bordercolorhover="0xffffff" borderalphahover="0.80"
	         fadeintime="0.150" fadeouttime="0.300" fadeincurve="1.100" fadeoutcurve="0.700"
	         onover=""
	         onhover="showtext(Click here to go to StateB)"
	         onout=""
	         ondown=""
	         onclick="set(view.fovmin,5);looktohotspot(centerfront,10);state_machine_keep_begin(tour);loadpano(stateB.xml,null,MERGE,BLEND(2),state_machine_keep_end(tour);push(autorotate.enabled);set(autorotate.enabled,false);push(view.fovmin);set(view.fovmin,50.34);lookat(38.45,24.01,50.34);wait(blend);lookto(0,0,65,smooth(100,100,100));pop(view.fovmin);pop(autorotate.enabled))"
	         onup=""
		>
	</hotspot>
<preview type="grid(cube,16,16,512,0xCCCCCC,0xFFFFFF,0x999999);" details="16" />

</krpano>

For this I assumed that the single quote being between the double quotes wouldn't be a problem, but the second if statement never even executes...weird! *confused*

Quoted

have limited the number of single actions to be executed at once to 2000 at the moment,
it is still a simple interpreter and the flashplayer is not that fast in this case and so too much
actions will slow down the viewer too much,

and I have made this limit also to catch up user coding faults when writing "endless loops"
(the flashplayer could run and block the system up to 15 seconds when in an endloss loop!)


I guess I can understand this limitation, you don't want to make it easy to deploy a pano with an infinite loop in it. I must admit that the only time I ran into the action overflow problem was when writing debugging and test routines.

Thanks again for a superb tool Klaus! those strict comparison operators have made my week!

steve

michel

Professional

Posts: 1,153

Location: ANDORRA

Occupation: TV

  • Send private message

6

Sunday, March 21st 2010, 4:51pm

Hi Steve,

Quoted

what is the proper way to set a variable to null?
I have tried set(foo); but it has no effect...
I have tried set(foo,); but it is like set(foo,""); (a string with nothing)....
It seems there is no way to set a variable to null ...
The only case I found to be possible is for variables set inside a plugin or an hotspot... removing the plugin or hotspot makes the variables to be null ...

SAlut.

Similar threads