Home AMX User Forum AMXForums Archive Threads AMX Applications and Solutions

Found a BUG in OR-Operator

Hi community
(Sorry, my english is very bad)

I belive, i found a bug in the order of OR-Operator (BOR, |)
There is my test code:
ROGRAM_NAME = 'Operator-Problem'

DEFINE_CONSTANT

integer nFixInt = $40
long    nFixLng = $40

DEFINE_VARIABLE

volatile integer nInt
volatile long    nLng

define_function Validate(char msg[], long result, integer value)
{
    if(result == value)
	send_string 0, "msg, itoa(result), '==', itoa(value), ' -> SUCCESS'"
    else
	send_string 0, "msg, itoa(result), '!=', itoa(value), ' -> FAILED'"
}

DEFINE_START

nInt = $40
nLng = $40

send_string 0, 'Testing BOR (|) ... (X=$40)'

// Variable
Validate('Testing var Integer: X | $20 => ', nInt    | $20, $60)
Validate('Testing var Long   : X | $20 => ', nLng    | $20, $60)
Validate('Testing fix Integer: X | $20 => ', nFixInt | $20, $60)
Validate('Testing fix Long   : X | $20 => ', nFixLng | $20, $60)
Validate('Testing literal    : X | $20 => ', $40     | $20, $60)
Validate('Testing var Integer: $20 | X => ', $20 | nInt,    $60)
Validate('Testing var Long   : $20 | X => ', $20 | nLng,    $60)
Validate('Testing fix Integer: $20 | X => ', $20 | nFixInt, $60)
Validate('Testing fix Long   : $20 | X => ', $20 | nFixLng, $60)
Validate('Testing literal    : $20 | X => ', $20 | $40,     $60)

And that's the result:

(0000022603) Testing BOR (|) ... (X=$40)
(0000022605) Testing var Integer: X | $20 => 96==96 -> SUCCESS
(0000022605) Testing var Long : X | $20 => 96==96 -> SUCCESS
(0000022605) Testing fix Integer: X | $20 => 96==96 -> SUCCESS
(0000022606) Testing fix Long : X | $20 => 96==96 -> SUCCESS
(0000022607) Testing literal : X | $20 => 96==96 -> SUCCESS
(0000022607) Testing var Integer: $20 | X => 32!=96 -> FAILED
(0000022608) Testing var Long : $20 | X => 32!=96 -> FAILED

(0000022609) Testing fix Integer: $20 | X => 96==96 -> SUCCESS
(0000022609) Testing fix Long : $20 | X => 96==96 -> SUCCESS
(0000022611) Testing literal : $20 | X => 96==96 -> SUCCESS

Is that a bug?
If yes, where i can report this problem, and what is the solution?

Comments

  • cmasoncmason Posts: 123
    Hi community
    (Sorry, my english is very bad)

    I belive, i found a bug in the order of OR-Operator (BOR, |)
    There is my test code:
    ROGRAM_NAME = 'Operator-Problem'
    
    DEFINE_CONSTANT
    
    integer nFixInt = $40
    long    nFixLng = $40
    
    DEFINE_VARIABLE
    
    volatile integer nInt
    volatile long    nLng
    
    define_function Validate(char msg[], long result, integer value)
    {
        if(result == value)
    	send_string 0, "msg, itoa(result), '==', itoa(value), ' -> SUCCESS'"
        else
    	send_string 0, "msg, itoa(result), '!=', itoa(value), ' -> FAILED'"
    }
    
    DEFINE_START
    
    nInt = $40
    nLng = $40
    
    send_string 0, 'Testing BOR (|) ... (X=$40)'
    
    // Variable
    Validate('Testing var Integer: X | $20 => ', nInt    | $20, $60)
    Validate('Testing var Long   : X | $20 => ', nLng    | $20, $60)
    Validate('Testing fix Integer: X | $20 => ', nFixInt | $20, $60)
    Validate('Testing fix Long   : X | $20 => ', nFixLng | $20, $60)
    Validate('Testing literal    : X | $20 => ', $40     | $20, $60)
    Validate('Testing var Integer: $20 | X => ', $20 | nInt,    $60)
    Validate('Testing var Long   : $20 | X => ', $20 | nLng,    $60)
    Validate('Testing fix Integer: $20 | X => ', $20 | nFixInt, $60)
    Validate('Testing fix Long   : $20 | X => ', $20 | nFixLng, $60)
    Validate('Testing literal    : $20 | X => ', $20 | $40,     $60)
    

    And that's the result:

    (0000022603) Testing BOR (|) ... (X=$40)
    (0000022605) Testing var Integer: X | $20 => 96==96 -> SUCCESS
    (0000022605) Testing var Long : X | $20 => 96==96 -> SUCCESS
    (0000022605) Testing fix Integer: X | $20 => 96==96 -> SUCCESS
    (0000022606) Testing fix Long : X | $20 => 96==96 -> SUCCESS
    (0000022607) Testing literal : X | $20 => 96==96 -> SUCCESS
    (0000022607) Testing var Integer: $20 | X => 32!=96 -> FAILED
    (0000022608) Testing var Long : $20 | X => 32!=96 -> FAILED

    (0000022609) Testing fix Integer: $20 | X => 96==96 -> SUCCESS
    (0000022609) Testing fix Long : $20 | X => 96==96 -> SUCCESS
    (0000022611) Testing literal : $20 | X => 96==96 -> SUCCESS

    Is that a bug?
    If yes, where i can report this problem, and what is the solution?

    Functions are destructive in respect to their arguments.

    In other words, when you plug a variable (nInt) into the function as an argument, it will change the value of the variable (nInt).

    If you "RETURN" the result instead of recycling the variable in the argument it should work as desired.
  • cmasoncmason Posts: 123
    Upon further review... the easiest way to make this work correctly is to just reset the variable before the expression.
    Validate('Testing var Integer: X | $20 => ', nInt    | $20, $60)
    Validate('Testing var Long   : X | $20 => ', nLng    | $20, $60)
    Validate('Testing fix Integer: X | $20 => ', nFixInt | $20, $60)
    Validate('Testing fix Long   : X | $20 => ', nFixLng | $20, $60)
    Validate('Testing literal    : X | $20 => ', $40     | $20, $60)
    nInt = $40
    Validate('Testing var Integer: $20 | X => ', $20 | nInt,    $60)
    nLng = $40
    Validate('Testing var Long   : $20 | X => ', $20 | nLng,    $60)
    Validate('Testing fix Integer: $20 | X => ', $20 | nFixInt, $60)
    Validate('Testing fix Long   : $20 | X => ', $20 | nFixLng, $60)
    Validate('Testing literal    : $20 | X => ', $20 | $40,     $60)
    
    

    I believe using the (OR) expression in the function's argument modifies the variable to the result of the expression ($60).

    You may wan to avoid using expressions with variables in function arguments just for that reason. It can cause unintended results.
  • The function 'Validate' is not the Problem
    this function is only a utility to show difference results on console...

    If you direct assign the operation as a term to the variable,
    the result 2 is wrong:

    Look:
    PROGRAM_NAME = 'Operator-Problem'
    
    DEFINE_VARIABLE
    
    volatile integer nValue1
    volatile integer nValue2
    volatile integer nResult
    
    DEFINE_START
    
    nValue1 = $40
    nResult = nValue1 | $20
    send_string 0, "'Result 1: ', itoa(nResult)" [b][color=green]// 96 => CORRECT[/color][/b]
    
    nValue1 = $40
    nResult = $20 | nValue1
    send_string 0, "'Result 2: ', itoa(nResult)" [b][color=red]// 32 => WRONG![/color][/b]
    
    nValue1 = $40; nValue2 = $20 
    nResult = nValue1 | nValue2
    send_string 0, "'Result 3: ', itoa(nResult)" [b][color=green]// 96 => CORRECT[/color][/b]
    
    nValue1 = $40; nValue2 = $20
    nResult = nValue2 | nValue1
    send_string 0, "'Result 4: ', itoa(nResult)" [b][color=green]// 96 => CORRECT[/color][/b]
    

    Can you see, what I mean?
  • cmasoncmason Posts: 123

    Can you see, what I mean?

    Yes, I do see what you mean now.

    Very strange. Especially because it only seems to error when it's a "CONSTANT OR VARIABLE" not "VARIABLE OR CONSTANT".

    I guess it's time for an AMX engineer to chime in on this one.
  • GregGGregG Posts: 251
    Nevermind, 96 should fit in 8 bits...
  • cmasoncmason Posts: 123
    GregG wrote: »
    Nevermind, 96 should fit in 8 bits...

    Yeah, and I even tried it with the numbers 1 OR 2, with the same results.

    It baffles my mind that a bug in such an inherent command could go unnoticed for so long.

    Admittedly, if and when I use bit wise operators with variables, I always express the variable 1st and the constant second (just seems to make sense in that order), therefore I've never encountered the issue.
  • integer int40;
    int40 = $40;
    
    send_string 0, "'$20 | int40: ', itoa($20 | int40)"; // => 32 ($20)
    send_string 0, "'type_cast($20) | int40: ', itoa(type_cast($20) | int40)"; // => 96 ($60)
    

    Interestingly, a type_cast on the constant works. Looks like the NetLinx interpreter isn't initializing variables that are evaluated after constants. I recommend reporting the bug to AMX, as they don't always respond to the forum: support@amx.com, or 800-932-6993
  • ericmedleyericmedley Posts: 4,177
    amclain wrote: »
    integer int40;
    int40 = $40;
    
    send_string 0, "'$20 | int40: ', itoa($20 | int40)"; // => 32 ($20)
    send_string 0, "'type_cast($20) | int40: ', itoa(type_cast($20) | int40)"; // => 96 ($60)
    

    Interestingly, a type_cast on the constant works. Looks like the NetLinx interpreter isn't initializing variables that are evaluated after constants. I recommend reporting the bug to AMX, as they don't always respond to the forum: support@amx.com, or 800-932-6993

    rememer - Type_cast has more to do with eliminating annoying warnings during comple than the actual running of the program.
  • DHawthorneDHawthorne Posts: 4,584
    This is yet another case of the internal type casts doing something funny under the hood that isn't clear on our end. If you do your BOR and assign it to a variable, then send it to your validate function it works fine. It's only when you do it inline as your function parameter that it fails. The fact that a type_cast on the second example works bears this out. There is some internal rule set that is converting the values in the expression that is not fully documented and unclear. It's not in the BOR, it's in how expressions are evaluated. I've seen this time and time again, and have learned just to code it a different way to get around it.
  • ericmedleyericmedley Posts: 4,177
    DHawthorne wrote: »
    This is yet another case of the internal type casts doing something funny under the hood that isn't clear on our end. If you do your BOR and assign it to a variable, then send it to your validate function it works fine. It's only when you do it inline as your function parameter that it fails. The fact that a type_cast on the second example works bears this out. There is some internal rule set that is converting the values in the expression that is not fully documented and unclear. It's not in the BOR, it's in how expressions are evaluated. I've seen this time and time again, and have learned just to code it a different way to get around it.

    agreed... as a genral rule I do not use type_cast. I just try to do it anotehr way. I don't like operating in the territory where you have to 'fool' the cmpliler into being okay with what your're doing.
  • ericmedley wrote: »
    rememer - Type_cast has more to do with eliminating annoying warnings during comple than the actual running of the program.

    Yes. But in this case it shows important information about the behavior of the bug so that it can be fixed by AMX (or understood by the community). The point is not that NetLinx programmers should be blindly using type_cast() to make the problem go away; the point is that type_cast() is not converting data widths but copying memory in this case. It behaves like it’s copying the $20 constant into the same lexical scope as the int40 local variable so they can be evaluated together.

    Here’s the same example using a different method that show’s it’s a problem with the scope the expression is evaluated in:
    send_string 0, "'$20 | int40: ', itoa($20 | int40)"; // => 32 ($20) - WRONG
    send_string 0, "'atoi(itoa(($20)) | int40: ', itoa(atoi(itoa(($20))) | int40)"; // => 96 ($60)
    

    And here’s a picture that shows what we can deduce is happening inside the “black box” (NetLinx interpreter):

  • cmasoncmason Posts: 123
    amclain wrote: »
    Yes. But in this case it shows important information about the behavior of the bug so that it can be fixed by AMX (or understood by the community). The point is not that NetLinx programmers should be blindly using type_cast() to make the problem go away; the point is that type_cast() is not converting data widths but copying memory in this case. It behaves like it’s copying the $20 constant into the same lexical scope as the int40 local variable so they can be evaluated together.

    Here’s the same example using a different method that show’s it’s a problem with the scope the expression is evaluated in:
    send_string 0, "'$20 | int40: ', itoa($20 | int40)"; // => 32 ($20) - WRONG
    send_string 0, "'atoi(itoa(($20)) | int40: ', itoa(atoi(itoa(($20))) | int40)"; // => 96 ($60)
    

    Interesting deduction my dear Watson...

    But it doesn't seem to explain why it functions correctly when the order of operation is reversed.

    i.e.
    send_string 0, "'int40 | $20: ', itoa(int40 | $20)"; // => 96 ($60) - CORRECT
  • cmason wrote: »
    Interesting deduction my dear Watson...
    But it doesn't seem to explain why it functions correctly when the order of operation is reversed.

    There may be conditional logic in the NetLinx interpreter that makes it order dependent, or there may be an error in the lexer/parser grammar that's causing the local variable to be classified as a constant (which isn't a constant, can't be found, and is never initialized). Unfortunately, we'd have to have access to the source code to be able to answer that question for certain.

    If this kind of thing interests you, I recommend the ebook How To Create Your Own Freaking Awesome Programming Language. I also have an unfinished NetLinx lexer and parser floating around if you're curious to see what those look like.
Sign In or Register to comment.