Author Topic: _UNSIGNED _INTEGER64 bug?  (Read 6240 times)

0 Members and 1 Guest are viewing this topic.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
_UNSIGNED _INTEGER64 bug?
« on: June 14, 2021, 12:01:35 am »
This is run on the previous version of QB64

E prints out as 1983905792,   it should be 16000000000  (16 billion)

E is an _UNSIGNED _INTEGER64 variable, it should be able to store 16 billion easily, as it maxes out at 18 billion or so.

is this thread related  https://www.qb64.org/forum/index.php?topic=3636.msg129794#msg129794

and here more problems with  _INTEGER64

https://www.qb64.org/forum/index.php?topic=2070.msg112998#msg112998

and here even more problems   https://www.qb64.org/forum/index.php?topic=1237.msg104539#msg104539

so i'm not imagining it.  _INTEGER64  is broken!!!!

I imagine that some spagetti code needs to be cleaned up in the compiler.

Anyone on the thread run the code yet?

Quote
DIM A AS _UNSIGNED LONG
DIM b AS _UNSIGNED INTEGER
DIM c AS _UNSIGNED INTEGER

DIM E AS _UNSIGNED _INTEGER64
DIM F AS _UNSIGNED LONG
DIM G AS _UNSIGNED LONG

b = 40000
c = 40000

A = b * c

PRINT A

F = 4000000000
G = 4000000000
E = F * G

PRINT E
« Last Edit: June 14, 2021, 01:54:32 am by NOVARSEG »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: _UNSIGNED _INTEGER64 bug?
« Reply #1 on: June 14, 2021, 01:47:26 am »
Quote
E prints out as 1983905792,   it should be 16000000000  (16 billion)

Shouldn’t it be 16,000,000,000,000,000,000?  That’s quite a bit larger than 16 billion…

The issue here, I imagine, is that you’re compiling with the 64-bit version.  32-bit QB64 uses 32-bit math registers, which gives us the limits posted in the wiki.  64-bit QB64 uses 64-bit math registers, which actually gives us lower limits of precision and a smaller variable range.

I’ve written about this before — it’s all in the way our gcc compiler does the math on 64-bit machines; it’s nothing in QB64 itself, nor any “glitch” which we can fix.  You might try ripping out the compiler we package and putting in a newer version, and see if the gcc guys have altered the behavior of the compiler, but this issue is simply out of our hands.

Easiest fix?  Just manually set your compiler to use the FPU math processors.  I’ve posted how a dozen times, so just search the forums for the code.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: _UNSIGNED _INTEGER64 bug?
« Reply #2 on: June 14, 2021, 02:08:44 am »
Steve

 you are correct (again)

18,446,744,073,709,551,615 max
16,000,000,000,000,000,000

So my comp is 32 bit.   OK  trying to simulate product of 2 _UNSIGNED LONG variables.

so the code  I posted should work on 64 bit machines.

Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Re: _UNSIGNED _INTEGER64 bug?
« Reply #3 on: June 14, 2021, 02:27:28 am »
@NOVARSEG
@SMcNeill

I think the code below speaks for itself

Code: QB64: [Select]
  1. f~& = 4000000000
  2. g~& = 4000000000
  3. e~&& = f~& * g~&
  4. PRINT USING "###,###,###,###,###,###,###"; e~&&
  5.  
  6. f~& = 4000000000
  7. g~& = 4000000000
  8. e~&& = 1~&& * f~& * g~&
  9. PRINT USING "###,###,###,###,###,###,###"; e~&&
  10.  
  11. f~&& = 4000000000
  12. g~&& = 4000000000
  13. e~&& = f~&& * g~&&
  14. PRINT USING "###,###,###,###,###,###,###"; e~&&

Offline luke

  • Administrator
  • Seasoned Forum Regular
  • Posts: 324
    • View Profile
Re: _UNSIGNED _INTEGER64 bug?
« Reply #4 on: June 14, 2021, 02:33:06 am »
No, this has nothing to do with 32 vs 64 bit versions - you get the same result with either.

What you're seeing is a side-effect of the rules for evaluating expressions, namely that the type at which an expression is evaluated depends on the inputs only, not the type of the variable that will hold the result.

This means when you multiply two Longs, you're asking the computer to do a 32 x 32 bit multiplication, which gives a 32 bit result. There is no provision in the language for the compiler to infer that you wanted a 64 bit multiplication because you're assigning the result to a 64 bit variable. Indeed in some cases it would be impossible to make such as inference, e.g. PRINT F * G.

What you want, then, is a way to ask the computer to perform a 64 bit multiplication. There are two ways:

1) Make one (or both) of your F, G variables an unsigned integer64. You'll then be asking for a 64 x 32 bit multiplication, which will cause the 32 bit value to be extended to 64 bits for a 64 x 64 bit multiplication, giving the expected result.

2) Pre-multiply by 1~&&, i.e. E = 1~&& * F * G. Similar to above, 1~&& is a 64 bit value which means the first multiplication gets done with 64 bits to result in 64 bits, which in turn causes the same behaviour with the seconds multiplication. It is important to use a pre-multiplication not post-multiplication (E = F * G * 1~&&) because by then it will be too late, and your result will already have been truncated.

EDIT: Ninja'd by RIchard - his code shows the options described above.
« Last Edit: June 14, 2021, 02:34:08 am by luke »

Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Re: _UNSIGNED _INTEGER64 bug?
« Reply #5 on: June 14, 2021, 02:46:19 am »
OR

Code: QB64: [Select]
  1. f~& = 4000000000
  2. g~& = 4000000000
  3. e~&& = CDBL(f~&) * g~&
  4. PRINT USING "###,###,###,###,###,###,###"; e~&&
  5.  

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: _UNSIGNED _INTEGER64 bug?
« Reply #6 on: June 14, 2021, 03:38:56 am »
from https://fruttenboel.verhoeven272.nl/asm/a86man.html



F7 /4  MUL rmv   Unsigned multiply (eDXeAX = eAX * regmem vword)

The Intel opcodes for multiply show that when two longs are multiplied, the result is in EDX and EAX.  This is because the CPU knows the result won't fit in a 32 bit register.  I'm sure the compiler could store EDX and EAX in a 64 bit register if it were available.

« Last Edit: June 14, 2021, 03:45:00 am by NOVARSEG »

Offline luke

  • Administrator
  • Seasoned Forum Regular
  • Posts: 324
    • View Profile
Re: _UNSIGNED _INTEGER64 bug?
« Reply #7 on: June 14, 2021, 03:40:31 am »
That jumps to floating-point operations though, which is a little annoying. It's a shame there's no _CINT64() function.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: _UNSIGNED _INTEGER64 bug?
« Reply #8 on: June 14, 2021, 04:19:55 am »
from https://www.felixcloutier.com/x86/mul

If the CPU has 64 bit registers  (RAX etc)

REX.W + F7 /4   MUL r/m64 Valid N.E.   Unsigned multiply (RDX:RAX ← RAX ∗ r/m64).

from https://www.cs.uaf.edu/2017/fall/cs301/lecture/09_11_registers.html
rax is the 64-bit,  (8 byte) size register.  It was added in 2003 during the transition to 64-bit processors.


 ( not tested)  Ok when the compiler sees E = F * G it should code

mov [temp +4],edx   ;most significant of F * G
mov [temp],eax   ;least significant of F * G
mov rax, [temp]  ;64 bit result.  E

but the compiler code  is ignoring edx and eax 

from above

RDX:RAX ← RAX ∗ r/m64).

this shows that if the CPU result of the multiply is in RDX:RAX then the multiplicands can not be 32 bit.  Is this rule compiler is following?  But two 32 bit value can be multiplied  by the CPU to give a 64 bit value.




« Last Edit: June 14, 2021, 04:47:57 am by NOVARSEG »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: _UNSIGNED _INTEGER64 bug?
« Reply #9 on: June 14, 2021, 09:45:40 pm »
@luke

Quote
Indeed in some cases it would be impossible to make such as inference, e.g. PRINT F * G.

PRINT F * G should work.

both F and G are unsigned longs and  the CPU actually does the multiplication properly.
 The CPU puts the product in EDX:EAX.  The compiler is ignoring EDX:EAX.

When the compiler sees PRINT  F * G it could code

mov [temp +4],edx   ;most significant of F * G
mov [temp],eax   ;least significant of F * G
PRINT [temp]  ;64 bit result.  ;  code simplified

For PRINT F * G  QB64 looks to be reading the EAX register only
 which is the least significant 4 bytes of the product F * G
example

F = 65536
G = 65536

PRINT F * G = 0 ' the largest value EAX can hold is 2 ^ 32 - 1
but EAX wraps around to all zeros (showing a carry to EDX) when it try's to
store 2 ^ 32

F = 65536
G = 65537

PRINT F * G = 65536 '=  2 ^ 32 - F * G

If QB64 compiler read EDX:EAX it would get

PRINT F * G correct.

So what we have now is work arounds, kind of a band-aid approach to programming.

« Last Edit: June 14, 2021, 09:50:11 pm by NOVARSEG »

Offline luke

  • Administrator
  • Seasoned Forum Regular
  • Posts: 324
    • View Profile
Re: _UNSIGNED _INTEGER64 bug?
« Reply #10 on: June 14, 2021, 09:48:20 pm »
I think you misunderstand me: this is by design. There is nothing to fix.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: _UNSIGNED _INTEGER64 bug?
« Reply #11 on: June 14, 2021, 09:50:59 pm »
The code was deliberately designed to give the wrong answer.

The print out of the product of 2 unsigned longs should be trivial.   How is anyone new to Qb64 (or any other basic with this problem) to know that there are reasons why they can't program in the way that seems most logical?  Computers are logical machines.

The CPU puts the product of 2 unsigned longs in EDX:EAX.   That is what QB64 should print out.  If it can't do that then the compiler needs to be fixed.

« Last Edit: June 15, 2021, 11:14:30 pm by NOVARSEG »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: _UNSIGNED _INTEGER64 bug?
« Reply #12 on: June 16, 2021, 12:04:44 am »
DIM c AS _UNSIGNED INTEGER

c = 65537
PRINT c

= 1

It is NOT possible to store 65536 or greater in an _UNSIGNED INTEGER. I think this was the case with QB45.   Looks like there is some compiler code that does a subtraction first and then the difference is put into c.

65537 - 65536 = 1

Well, is this an advantage or disadvantage?  For a student learning computer science this is a disadvantage because it is hiding the real operation of a 16 bit register.  The compiler should NOT allow c = 65536 or greater

Basically the compiler is 2nd guessing what the programmer thinks should happen.  Ok you have a value of 65535 in a 16 bit register, now add 1
65535 + 1 = 0     you don't need the compiler to 2nd guess what you already know will happen.

What about things like "subscript out of range" error?  The error is there to alert the programmer the code is incorrect.

c = 65536 is also "out of range" 
****************************************
Ok I'm taking a break from computing for a while

I'm too grumpy right now.





« Last Edit: June 16, 2021, 01:16:21 am by NOVARSEG »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: _UNSIGNED _INTEGER64 bug?
« Reply #13 on: June 16, 2021, 11:36:56 am »
Code: QB64: [Select]
  1.  
  2. For c = 65534 To 65536
  3.     Print c
  4.  
  5.  
  6.  

LOL

FellippeHeitor

  • Guest
Re: _UNSIGNED _INTEGER64 bug?
« Reply #14 on: June 16, 2021, 11:46:03 am »
From "Unsigned integers, and why to avoid them" - https://www.learncpp.com/cpp-tutorial/unsigned-integers-and-why-to-avoid-them/
Quote
Any number bigger than the largest number representable by the type simply “wraps around” (sometimes called “modulo wrapping”, or more obscurely, “saturation”)

From "Integer overflow" - https://en.wikipedia.org/wiki/Integer_overflow
Quote
In C, unsigned integer overflow is defined to wrap around, while signed integer overflow causes undefined behavior

You guys have a point there?