Converting from PowerBasic

General FreeBASIC programming questions.
Mike Trader
Posts: 25
Joined: Feb 02, 2008 22:10

Post by Mike Trader »

3 billion times 3 is not a number a 32-bit unsigned integer is capable of holding
Thats the point I think. If I try to store a 10 digit phone number in a LONG I am going to have an overflow. It might not happen until the result gets back to memory, but a LONG integer does not have enough bits to hold all 10 digit phone numbers.

Michael,
If anyone was going to implement this in a compiler I would expect M$ or Borland perhaps, but PowerBASIC. It seems like the tail wagging the dog.

It is very interesting to hear your arguments. You are obviously correct in what you are saying, but why do most other languages not do this?
Did it evolve this way from before the FPU perhaps?
cha0s
Site Admin
Posts: 5319
Joined: May 27, 2005 6:42
Location: USA
Contact:

Post by cha0s »

Code: Select all

dim as uinteger a = 3000000000, b = 3
dim as uinteger foo = a * (b / b)
print foo
:P
notthecheatr
Posts: 1759
Joined: May 23, 2007 21:52
Location: Cut Bank, MT
Contact:

Post by notthecheatr »

Code: Select all

Dim As Uinteger a = 3000000000, b = 3
Dim As Uinteger foo = a * b / b
Print foo

Sleep
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Post by MichaelW »

Code: Select all

Dim As Uinteger a = 3000000000, b = 3
asm nop
Dim As Uinteger foo = a * (b / b)
asm nop
Print foo

Code: Select all

nop
push 0
push dword ptr [ebp-8]
fild qword ptr [esp]
add esp, 8
push 0
push dword ptr [ebp-12]
fild qword ptr [esp]
add esp, 8
push 0
push dword ptr [ebp-12]
fild qword ptr [esp]
add esp, 8
fxch st(1)
fdivrp
fxch st(1)
fmulp
sub esp, 8
fistp qword ptr [esp]
pop dword ptr [ebp-16]
add esp, 4
nop
Yet another industry standard result screwed up by the FPU.
notthecheatr
Posts: 1759
Joined: May 23, 2007 21:52
Location: Cut Bank, MT
Contact:

Post by notthecheatr »

This code:

Code: Select all

Dim As Uinteger a = 3000000000, b = 3
asm nop
Dim As Uinteger foo = a * (b / b)
asm nop
Print foo
 
is not what I'm talking about. In that case, the result is mathematically correct as expected because you divide b by b so you get 1. I'm talking about

Code: Select all

Dim As Uinteger a = 3000000000, b = 3
asm nop
Dim As Uinteger foo = (a * b) / b
asm nop
Print foo
which is entirely different. Now I could be wrong, perhaps the FPU does give the overflow result for that, but as far as I've been able to tell it doesn't.
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Post by MichaelW »

My point was that PB is not the only compiler using the FPU for some integer computations.

Your code returns 136688469, by doing an integer multiply and then passing only the lower 32 bits to the FPU to perform the division. With no parenthesis FBC follows the normal operator precedence, and generates the same code.

Code: Select all

mov dword ptr [ebp-8], -1294967296
mov dword ptr [ebp-12], 3
nop
mov eax, dword ptr [ebp-12]
mul dword ptr [ebp-8]
push 0
push eax
fild qword ptr [esp]
add esp, 8
push 0
push dword ptr [ebp-12]
fild qword ptr [esp]
add esp, 8
fxch st(1)
fdivrp
sub esp, 8
fistp qword ptr [esp]
pop dword ptr [ebp-16]
add esp, 4
nop
For:

Dim As Uinteger foo = a * (b \ b)

FBC generates this code, integer instructions only, producing the correct result.

Code: Select all

mov dword ptr [ebp-8], -1294967296
mov dword ptr [ebp-12], 3
nop
mov eax, dword ptr [ebp-12]
xor edx, edx
div dword ptr [ebp-12]
mul dword ptr [ebp-8]
mov dword ptr [ebp-16], eax
nop
And for:

Dim As Uinteger foo = a * b \ b

Code: Select all

mov dword ptr [ebp-8], -1294967296
mov dword ptr [ebp-12], 3
nop
mov eax, dword ptr [ebp-12]
mul dword ptr [ebp-8]
xor edx, edx
div dword ptr [ebp-12]
mov dword ptr [ebp-16], eax
nop
Specifically clearing the upper 32 bits of the product, and producing the overflow result.

So by manipulating the code you can get either result, with or without the FPU. PB supports the same operators, operator precedence, etc, presumably doing the same things, so I suspect this would work in PB as well.
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Post by MichaelW »

Mike Trader wrote: If I try to store a 10 digit phone number in a LONG I am going to have an overflow. It might not happen until the result gets back to memory, but a LONG integer does not have enough bits to hold all 10 digit phone numbers.
The largest 10-digit number that will fit in a LONG is 2147483647 (&H7FFFFFFF), and the largest 10-digit number that will fit in a DWORD is 4294967295 (&HFFFFFFFF). Why not store it in a QUAD?
why do most other languages not do this?
I’m not clear on what “this” is.
relsoft
Posts: 1767
Joined: May 27, 2005 10:34
Location: Philippines
Contact:

Post by relsoft »

Well, some of us like to have overflows and that overflows in calcs should get back to 0 then 1 then 2....

Why? Because it has it's uses like counters perhaps?
Mike Trader
Posts: 25
Joined: Feb 02, 2008 22:10

Post by Mike Trader »

I use QUADs for phone numbers yes. I was illustrating that as developers we know what the range of values is for a DataType and why would anyone use a DataType that is not capable of holding the result of a calculation in it?

Am I understanding your point correctly, in that it doesn't matter if the intermediate result is beyond the scope of the DataType as long as the final result written to memory IS?

If that is the case, I am wondering why most other languages do not use the FPU for unsigned integer calulations too?

In fact, leaving aside the overflow issue, why not use the FPU for signed integer calcs and everything else too?
cevpegge
Posts: 74
Joined: Feb 12, 2007 10:19
Location: Wales
Contact:

Post by cevpegge »

That is a very interesting question Mike. It is really down to the dual architecture of FPU and CPU which were at one time totally separate chips. The CPU managed all the adressing and indexing while the FPU just crunched the data. So today there is no direct connection between the CPU registers and the FPU registers. Data has to be transferred between the two via memory. The consequence is that the calculations required for indexing, pointering and arrays etc are most efficiently done in the CPU, - and of course bitwise operations are not directly available on the FPU.
cha0s
Site Admin
Posts: 5319
Joined: May 27, 2005 6:42
Location: USA
Contact:

Post by cha0s »

If you want to work with infinite* numbers, just use floating-point.

*Obviously, they are physical and therefore finite but the range is so large that for 99.9% of applications, it can be considered as infinite, especially double.
cevpegge
Posts: 74
Joined: Feb 12, 2007 10:19
Location: Wales
Contact:

Post by cevpegge »

One of the beauties of the FPU is that it can represent infinity, also positive and negative zero, which is very useful for trigonometry. High level languages usually struggle with these fundamental concepts yet they are common place in maths and engineering.
C
Posts: 104
Joined: Mar 13, 2006 2:17

Post by C »

If someone was up to coding it there could be an option to use the fpu for calculations. Personally I don't see the point though since you can just use a larger data type or GMP.
cevpegge
Posts: 74
Joined: Feb 12, 2007 10:19
Location: Wales
Contact:

Post by cevpegge »

FPU code is very good for crunching simple expressions in one go. Organising data efficiently on the FPU stack is something of a puzzle but very satisfying knowing you get the ultimate performance. I think small nuggets of this kind of Assember, embedded in Basic are quite tolerable. - Like Latin phrases :)

Some short examples:

http://www.jose.it-berater.org/smfforum ... pic=1368.0
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Post by MichaelW »

Mike Trader wrote: Am I understanding your point correctly, in that it doesn't matter if the intermediate result is beyond the scope of the DataType as long as the final result written to memory IS?
It does matter. For the final result of an integer calculation to be mathematically correct each result must fit into whatever data type it is stored in. Depending on the calculation and how it is performed, the processor may automatically store the intermediate results in a larger data type than the operands or the final result. For the calculation:

x = a * b / b

Where x, a, and b are 32-bit integers, a=3,000,000,000 and b=3, even though the final result will fit in a 32-bit integer, the intermediate result from the multiply operation will not. Doing this on the CPU, one obvious coding (using MASM code) would be:

Code: Select all

mov eax, a
mov ecx, b
mul ecx
div ecx
mov x, eax
Where the MUL instruction stores the 64-bit result in the EDX:EAX register pair, and the DIV instruction uses the EDX:EAX register pair as the dividend and returns the quotient in EAX.

Doing this on the FPU, one obvious coding would be:

Code: Select all

fild a
fimul b
fidiv b
fistp x
The “i” in the instruction mnemonics indicates that the memory operand is an integer instead of a real. Values loaded into the FPU, whether integer, floating-point or BCD, are converted to the 80-bit real number format that the FPU uses internally, and the intermediate results are stored internally in this same format. This format includes a 64-bit significand, 15 exponent bits, and a sign bit. The 64-bit significand allows the FPU to handle the full range of values for 64-bit integers. The last instruction converts the final result from the internal format to a 32-bit integer and stores it in x.
why not use the FPU for signed integer calcs and everything else too?
One reason to avoid using the FPU for everything is execution speed. Even if the system has an FPU, for integer operations the CPU is generally faster, and in some cases much faster.
Post Reply