Author Topic: Math bug with _UNSIGNED _BIT and _UNSIGNED LONG variable types  (Read 3462 times)

0 Members and 1 Guest are viewing this topic.

Offline Raven_Singularity

  • Forum Regular
  • Posts: 158
    • View Profile
I ran into a math bug in my program, which I tracked down to improper handling of math expressions that contain _UNSIGNED LONG variables.  If any part of a math expression contains an _UNSIGNED LONG variable, it forces the entire math expression to be evaluated as _UNSIGNED LONG.  Doesn't make any difference if the result is to be PRINTed or stored in a signed variable.



Sample QB64 Code

Code: QB64: [Select]
  1. foo~% = 10
  2. PRINT (foo~% + 10) * -1
  3.  
  4. bar~&& = 10
  5. PRINT (bar~&& + 10) * -1
  6.  
  7. baz~& = 10
  8. PRINT (baz~& + 10) * -1



Expected Output

Code: QB64: [Select]
  1. -20
  2. -20
  3. -20



Actual Output

Code: QB64: [Select]
  1. -20
  2. -20
  3. 4294967276
« Last Edit: April 12, 2019, 04:38:53 pm by Raven_Singularity »

Offline Ashish

  • Forum Resident
  • Posts: 630
  • Never Give Up!
    • View Profile
Re: Math bug with _UNSIGNED LONG variable type
« Reply #1 on: April 10, 2019, 02:57:55 am »
Confirmed. It might be a glitch which can be easily fixed by QB64 Team.
if (Me.success) {Me.improve()} else {Me.tryAgain()}


My Projects - https://github.com/AshishKingdom?tab=repositories
OpenGL tutorials - https://ashishkingdom.github.io/OpenGL-Tutorials

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: Math bug with _UNSIGNED LONG variable type
« Reply #2 on: April 10, 2019, 11:37:56 am »
Doesn't make any difference if the result is to be PRINTed or stored in a signed variable.

I disagree slightly with your last remark.  If you assign the result to a LONG variable (signed) and print that variable, you get the correct result (at least, I did).  Nevertheless, it is a bug, and a remarkable find of yours.

Code: QB64: [Select]
  1. foo~% = 10
  2. PRINT (foo~% + 10) * -1
  3.  
  4. bar~&& = 10
  5. PRINT (bar~&& + 10) * -1
  6.  
  7. baz~& = 10
  8. PRINT (baz~& + 10) * -1
  9.  
  10. baza& = (baz~& + 10) * -1
  11. PRINT baza&
  12.  

Result:
-20
-20
 4294967276
-20

Offline Jack002

  • Forum Regular
  • Posts: 123
  • Boss, l wanna talk about arrays
    • View Profile
Re: Math bug with _UNSIGNED LONG variable type
« Reply #3 on: April 10, 2019, 12:04:09 pm »
I found if you change -1 to -1.0 it behaves

baz~& = 10
PRINT (baz~& + 10) * -1.0


I don't know if that's helpful

(unsigned types being multiplied by negative numbers, seems this jumps types, can be a problem sometimes)
QB64 is the best!

Offline Raven_Singularity

  • Forum Regular
  • Posts: 158
    • View Profile
Re: Math bug with _UNSIGNED LONG variable type
« Reply #4 on: April 10, 2019, 12:21:24 pm »
Confirmed. It might be a glitch which can be easily fixed by QB64 Team.

Thanks for confirming.


I disagree slightly with your last remark.  If you assign the result to a LONG variable (signed) and print that variable, you get the correct result (at least, I did).  Nevertheless, it is a bug, and a remarkable find of yours.

Hmm...

In my actual code where I discovered the bug, I had a data type containing _UNSIGNED LONG variables.  I was then doing a math expression with those variables with the result stored in a signed variable (I think a _FLOAT or _INTEGER64, maybe another type) and it was forcing the answer to be unsigned.  In this case I was doing -Variable and then -1 * Variable with broken results.  I then tried forcing the type with INT(), no help.  Then I tried putting parenthesis around the unsigned variable to remove its type, but it didn't help.  The entire math expression was always forced to be unsigned.

I ended up changing my 20 or so _UNSIGNED LONG variables/data types to all be LONG to work around the bug.  At first I thought the behaviour might have been intentional, but it seems to only affect _UNSIGNED LONG type.

I'll do some more poking around later to see how I got the wrong answer when storing the result into a signed variable, because I know that can happen, as that's how I discovered it.


I found if you change -1 to -1.0 it behaves

baz~& = 10
PRINT (baz~& + 10) * -1.0


I don't know if that's helpful

That's very peculiar, and definitely useful information.  Integer vs. floating point affecting the result being signed or unsigned seems strange.  Probably there are multiple issues with how QB64 is interpretting math expressions containing mixed types.

From a "makes sense" perspective, all math expressions should be calculated with maximum precision, then the result forced to conform with any requested data type.

(unsigned types being multiplied by negative numbers, seems this jumps types, can be a problem sometimes)

That was why I put the unsigned variable in parenthesis and with integer math (var + 10), but it makes no difference for this bug.  The expression must do parenthesis first, so by the time it gets to the *  -1 it should already be a non-variable "20", but it remains an unsigned 20 somehow.


Edit:
I just thought about it more, and I think the reason using 1.0 instead of 1 works around the bug is because 1.0 is forcing it to evaluate as a float, and floats can't be unsigned.  That said, using INT() should also force it to be signed, but it keeps it as unsigned.  More hmm...
« Last Edit: April 10, 2019, 12:35:21 pm by Raven_Singularity »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Math bug with _UNSIGNED LONG variable type
« Reply #5 on: April 10, 2019, 01:09:18 pm »
Looking into the c translation of the code doesn't show us any sort of obvious glitch with the process.  What we basically translate down into is the following:

Code: QB64: [Select]
  1. *__UINTEGER_FOO= 10 ;
  2. tqbs=qbs_new(0,0);
  3. qbs_set(tqbs,qbs_add(qbs_str((int64)((*__UINTEGER_FOO+ 10 )* -1 )),qbs_new_txt(" ")));
  4. if (new_error) goto skip1;
  5. makefit(tqbs);
  6. qbs_print(tqbs,0);
  7. qbs_print(nothingstring,1);
  8. skip1:
  9. qbs_free(tqbs);
  10. qbs_cleanup(qbs_tmp_base,0);
  11. *__UINTEGER64_BAR= 10 ;
  12. tqbs=qbs_new(0,0);
  13. qbs_set(tqbs,qbs_add(qbs_str((int64)((*__UINTEGER64_BAR+ 10 )* -1 )),qbs_new_txt(" ")));
  14. if (new_error) goto skip2;
  15. makefit(tqbs);
  16. qbs_print(tqbs,0);
  17. qbs_print(nothingstring,1);
  18. skip2:
  19. qbs_free(tqbs);
  20. qbs_cleanup(qbs_tmp_base,0);
  21. *__ULONG_BAZ= 10 ;
  22. tqbs=qbs_new(0,0);
  23. qbs_set(tqbs,qbs_add(qbs_str((int64)((*__ULONG_BAZ+ 10 )* -1 )),qbs_new_txt(" ")));
  24. if (new_error) goto skip3;
  25. makefit(tqbs);
  26. qbs_print(tqbs,0);
  27. qbs_print(nothingstring,1);
  28. skip3:
  29. qbs_free(tqbs);
  30. qbs_cleanup(qbs_tmp_base,0);
  31. sub_end();
  32. }

The 3 important lines to really take note of are these:

qbs_set(tqbs,qbs_add(qbs_str((int64)((*__UINTEGER_FOO+ 10 )* -1 )),qbs_new_txt(" ")));
qbs_set(tqbs,qbs_add(qbs_str((int64)((*__UINTEGER64_BAR+ 10 )* -1 )),qbs_new_txt(" ")));
qbs_set(tqbs,qbs_add(qbs_str((int64)((*__ULONG_BAZ+ 10 )* -1 )),qbs_new_txt(" ")));

All 3 of these basically translate into the same thing:  Do the math, cast the value to a _INTEGER64, and then turn it into a temp string behind the scene so we can format it with make_fit and qbs_print which comes after.

At this point, I'd have to think the issue is either in the qbs_str function, or else it's actually a glitch which the c-compiler itself might be suffering from.  I really don't think it's something simple like a translation glitch which is causing the issue...
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Raven_Singularity

  • Forum Regular
  • Posts: 158
    • View Profile
Re: Math bug with _UNSIGNED LONG variable type
« Reply #6 on: April 10, 2019, 01:25:56 pm »
It's especially mysterious as it only affects _UNSIGNED LONG which is the mid-sized variable.  INTEGER and _INTEGER64 work fine, which are smaller and bigger.  I would expect to see the glitch on the outliers, INTEGER or _INTEGER64.  I was also using _UNSIGNED BYTE in my code, not sure if those were causing the bug or not (I'm not at my PC to check right now).


_BYTE, INTEGER, LONG, _INTEGER64

Are these all the integer types in QB64?

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Math bug with _UNSIGNED LONG variable type
« Reply #7 on: April 10, 2019, 01:29:39 pm »
As if this wasn't an odd enough glitch to sort out, check this little doozy of a snippet out and see what you think of it:

Code: QB64: [Select]
  1. baz~& = 10
  2. PRINT (baz~& + 10) * -1
  3.  
  4. IF (baz~& + 10) * -1 = -20 THEN PRINT "Yep" ELSE PRINT "Nope"
  5. IF (baz~& + 10) * -1 = 4294967276 THEN PRINT "Yep" ELSE PRINT "Nope"
  6. IF -20 = 4294967276 THEN PRINT "Yep" ELSE PRINT "Nope"
  7. IF 4294967276 = -20 THEN PRINT "Yep" ELSE PRINT "Nope"

Anyone care to guess what the results of those IF statements are, before running it?  I'll bet a dollar against a doughnut that you'll guess wrong...

WTH is going on here???

That's one magical formula!!



(And, for Raven, who isn't at his computer at the moment to run the code, the results are:  Yep, Yep, Nope, Nope.)

Somehow, QB64 calculates the formula so that it evaluates to TRUE in both the checks above, but then whatever result that value gives ends up FALSE (as it should be), no matter which way we present it.  In those first 2 IF statements, the value is swapping from signed to unsigned automagically!!
« Last Edit: April 10, 2019, 01:33:16 pm by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Raven_Singularity

  • Forum Regular
  • Posts: 158
    • View Profile
Re: Math bug with _UNSIGNED LONG variable type
« Reply #8 on: April 10, 2019, 01:42:37 pm »
Code: QB64: [Select]
  1. IF (baz~& + 10) * -1 = -20 THEN PRINT "Yep" ELSE PRINT "Nope"
  2. IF (baz~& + 10) * -1 = 4294967276 THEN PRINT "Yep" ELSE PRINT "Nope"

Those both being true hurts my brain!  Lol.

It seems the _UNSIGNED on the left forces the right value to also be _UNSIGNED.  I thought BASIC lets you compare disparate data types, like IF foo% = bar! THEN... but does that force the bar! to convert to foo%'s % type?

I'm very confused.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Math bug with _UNSIGNED LONG variable type
« Reply #10 on: April 10, 2019, 01:50:19 pm »
Question:  Is everyone testing this on Windows?  Or is this a glitch which carries over to Linux and Mac as well?
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Raven_Singularity

  • Forum Regular
  • Posts: 158
    • View Profile
Re: Math bug with _UNSIGNED LONG variable type
« Reply #11 on: April 10, 2019, 04:29:21 pm »
I'm on Windows 10.

I currently only have access to GNU+Linux in text mode.

Offline Jack002

  • Forum Regular
  • Posts: 123
  • Boss, l wanna talk about arrays
    • View Profile
Re: Math bug with _UNSIGNED LONG variable type
« Reply #12 on: April 10, 2019, 04:42:27 pm »
WIN 10, and I'm seeing what all of you are seeing.


Who knew the top two were yep and yep!
QB64 is the best!

Offline Raven_Singularity

  • Forum Regular
  • Posts: 158
    • View Profile
Re: Math bug with _UNSIGNED LONG variable type
« Reply #13 on: April 10, 2019, 10:01:59 pm »
So I have done some more digging into the bug and it gets even more interesting!


Code to execute

Code: QB64: [Select]
  1. PRINT "PRINTING (UNSIGNED_VARIABLE * -1)"
  2. a~` = 1: PRINT "_BIT", a~` * -1
  3. a~%% = 1: PRINT "_BYTE", a~%% * -1
  4. a~% = 1: PRINT " INTEGER", a~% * -1
  5. a~& = 1: PRINT " LONG", a~& * -1
  6. a~&& = 1: PRINT "_INTEGER64", a~&& * -1
  7. PRINT "STORING (LONG * -1) IN VARIABLE"
  8. a` = a~& * -1: PRINT "_BIT", a`
  9. a%% = a~& * -1: PRINT "_BYTE", a%%
  10. a% = a~& * -1: PRINT " INTEGER", a%
  11. a& = a~& * -1: PRINT " LONG", a&
  12. a&& = a~& * -1: PRINT "_INTEGER64", a&&
  13. a! = a~& * -1: PRINT " SINGLE", a!
  14. a# = a~& * -1: PRINT " DOUBLE", a#
  15. a## = a~& * -1: PRINT "_FLOAT", a##
  16. a$ = STR$(a~& * -1): PRINT " STRING", a$
  17.  
  18. OPEN "Output.txt" FOR OUTPUT AS #1
  19. PRINT #1, "PRINTING (UNSIGNED_VARIABLE * -1)"
  20. PRINT #1, ""
  21. a~` = 1: PRINT #1, "_BIT", a~` * -1
  22. a~%% = 1: PRINT #1, "_BYTE", a~%% * -1
  23. a~% = 1: PRINT #1, " INTEGER", a~% * -1
  24. a~& = 1: PRINT #1, " LONG", a~& * -1
  25. a~&& = 1: PRINT #1, "_INTEGER64", a~&& * -1
  26. PRINT #1, ""
  27. PRINT #1, ""
  28. PRINT #1, "STORING (LONG * -1) IN VARIABLE"
  29. PRINT #1, ""
  30. a` = a~& * -1: PRINT #1, "_BIT", a`
  31. a%% = a~& * -1: PRINT #1, "_BYTE", a%%
  32. a% = a~& * -1: PRINT #1, " INTEGER", a%
  33. a& = a~& * -1: PRINT #1, " LONG", a&
  34. a&& = a~& * -1: PRINT #1, "_INTEGER64", a&&
  35. a! = a~& * -1: PRINT #1, " SINGLE", a!
  36. a# = a~& * -1: PRINT #1, " DOUBLE", a#
  37. a## = a~& * -1: PRINT #1, "_FLOAT", a##
  38. a$ = STR$(a~& * -1): PRINT #1, " STRING", a$
  39.  


Output on Windows 10

Code: QB64: [Select]
  1. PRINTING (UNSIGNED_VARIABLE * -1)
  2.  
  3. _BIT           4294967295
  4. _BYTE         -1
  5.  INTEGER      -1
  6.  LONG          4294967295
  7. _INTEGER64    -1
  8.  
  9.  
  10. STORING (LONG * -1) IN VARIABLE
  11.  
  12. _BIT          -1
  13. _BYTE         -1
  14.  INTEGER      -1
  15.  LONG         -1
  16. _INTEGER64     4294967295
  17.  SINGLE        4.294967E+09
  18.  DOUBLE        4294967295
  19. _FLOAT         4294967295
  20.  STRING        4294967295


Conclusion
  • Printing out (_UNSIGNED LONG * -1) or (_UNSIGNED _BIT * -1) results in the error
  • Storing (_UNSIGNED LONG * -1) or (_UNSIGNED _BIT * -1) into floating point, _INTEGER64, or string variables results in the error.


In my app I was storing (_UNSIGNED LONG * -1) into a _FLOAT variable, so I experienced the bug.
« Last Edit: April 10, 2019, 10:07:57 pm by Raven_Singularity »