Discussion:
int64 := integer(65535) * integer(65535); // = negative :(
(too old to reply)
Skybuck Flying
2011-06-21 22:50:20 UTC
Permalink
Hello,

The following code becomes negative in Delphi XE:

var
a : integer;
b : integer;
c : int64;

a := 65535;
b := 65535;
c := a * b; // negative :((((

Why does Delphi XE do such a stupid thing ?!?

The c is big enough to hold the result ?!

Yes I know Delphi XE is still 32 bit, but can't it simply do a 64 bit
calculation when it sees the left side is 64 bit ?!?

What would be a good solution for this apperently common problem ?

Bye,
Skybuck.
Skybuck Flying
2011-06-21 22:55:52 UTC
Permalink
Well I don't want to fok up my code too much by introducing stupid local
variables just to solve this annoying problem, nor do I want to change any
variable types, this I come to the following solution:

function ToInt64( ParaValue : integer ) : int64;
begin
result := ParaValue;
end;

var
a : integer;
b : integer;
c : int64;

a := 65535;
b := 65535;
c := ToInt64(a) * ToInt64(b);

This solves it quite nicely.

Perhaps ToInt64 could even be inlined ! ;) :)

Yes that seems to work too nice ! ;) =D

Bye,
Skybuck.
Arivald
2011-06-22 09:33:58 UTC
Permalink
Post by Skybuck Flying
Hello,
var
a : integer;
b : integer;
c : int64;
a := 65535;
b := 65535;
c := a * b; // negative :((((
Why does Delphi XE do such a stupid thing ?!?
It is not stupid, standard overflow.
Negativeness is caused by standard binary representation of integers,
one overflowing "1" make it look like negative.

If You enable overflow checking you will be notified about such errors.
Post by Skybuck Flying
The c is big enough to hold the result ?!
Yes I know Delphi XE is still 32 bit, but can't it simply do a 64 bit
calculation when it sees the left side is 64 bit ?!?
Left side NEVER affects right side type! Conversion to left side type
happens AFTER right side expression is calculated.
Post by Skybuck Flying
What would be a good solution for this apperently common problem ?
Not very common. Most programmers know limits of types.

As solution, cast one or both operands to wider type, example:

c := int64(a) * int64(b);
--
Arivald
Skybuck Flying
2011-06-22 20:37:51 UTC
Permalink
Post by Skybuck Flying
What would be a good solution for this apperently common problem ?
Not very common. Most programmers know limits of types.

As solution, cast one or both operands to wider type, example:

c := int64(a) * int64(b);

^ That should trigger a variable overflow.

^ Allowing such typecasts is nonsense.

It's like typecasting an 8 byte record over a 4 byte record.

I cannot understand why such code would suddenly work ;)

Bye,
Skybuck.
Arivald
2011-06-23 08:31:28 UTC
Permalink
Post by Arivald
Post by Skybuck Flying
What would be a good solution for this apperently common problem ?
Not very common. Most programmers know limits of types.
c := int64(a) * int64(b);
^ That should trigger a variable overflow.
No way. Nothing is saved to "a"!
Compiler will prepare 2 64-bit registers, load "a" to one, "b" to
second, do math in registers, then save 64-bit register to 64-bit
variable "c".
Post by Arivald
^ Allowing such typecasts is nonsense.
What? Casting to wider type is always safe.
Post by Arivald
It's like typecasting an 8 byte record over a 4 byte record.
You did not understand? Integers (variables "a" & "b") are 4 bytes,
Int64 (variable "c") is 8 bytes... cast is safe, because narrower type
is casted to wider type. Compiler automatically make such casts in many
times, when two operands have different wide, compiler cast narrower to
wider type..
Post by Arivald
I cannot understand why such code would suddenly work ;)
I see... Why I'm not surprised?
--
Arivald
Skybuck Flying
2011-06-23 16:40:57 UTC
Permalink
It works because of luck.

In other situation it will not work.

Examples are fields in records and parameters on the stack.

Overflows will occur.

Thus using typecasts like your example is a very bad programming practice
and should not be promoted by Delphi.

I also highly doubt your register explaination... I am pretty sure Delphi is
a 32 bit compiler and doesn't even know about 64 bit registers, instead it
has it's own 64 bit multiplication software.

So please don't pretend you know anything about Delphi because you clearly
do not.

Bye,
Skybuck.
Skybuck Flying
2011-06-23 16:54:20 UTC
Permalink
This code doesn't even required a typecast and it already overflows:

// *** begin of program ***

program Project1;

{$APPTYPE CONSOLE}

uses
SysUtils;


procedure test( p : Plongword );
begin
writeln( p^ );
end;


var
b : byte;
a : byte;
c : byte;
d : byte;

begin
try
b := 255;
a := 255;
c := 255;
d := 255;
test( @b ); // parameter overflow

except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.

// *** end of program ***

I can vaguely remember other situations where using typecasts also triggered
all kinds of overflows.

Perhaps some even these are fixed in the Delphi XE...

You've been warned though ;)

Bye,
Skybuck :)
Skybuck Flying
2011-06-23 17:09:37 UTC
Permalink
Other reasons why these typecasts are dangerous:

int64(-1);
int64(longword($FFFFFF) );
int64(integer($FFFFFFFF) );

^ Can lead to different results.

Simply blindly converting some type to a higher type can lead to bugs.

There probably was a parameter overflow bug potential somewhere but I can't
remember it so lucky you ;)

Bye,
Skybuck.
David Kerber
2011-06-23 18:31:32 UTC
Permalink
[This followup was posted to alt.comp.lang.borland-delphi and a copy was
sent to the cited author.]
Post by Skybuck Flying
int64(-1);
int64(longword($FFFFFF) );
int64(integer($FFFFFFFF) );
^ Can lead to different results.
Simply blindly converting some type to a higher type can lead to bugs.
True, but if you use them properly rather than blindly, then there's no
trouble. Typecasts are standard in nearly all languages.
Arivald
2011-06-24 10:21:29 UTC
Permalink
Post by Skybuck Flying
int64(-1);
int64(longword($FFFFFF) );
int64(integer($FFFFFFFF) );
^ Can lead to different results.
Simply blindly converting some type to a higher type can lead to bugs.
Moron... whee in your example You cast to wider type with error? Only
one error is on casting $FFFFFFFF to integer.
Post by Skybuck Flying
There probably was a parameter overflow bug potential somewhere but I
can't remember it so lucky you ;)
Lucky? You are lucky You still alive... Such dumb people like You often
die in young age... Maybe You should check how electricity in your house
work? Do us favor...
--
Arivald
Skybuck Flying
2011-06-24 15:01:01 UTC
Permalink
Nope,

You the idiot and here is why:

Delphi does not have a real uint64 type.

Therefore typecasting longwords to uint64 won't work.

It's as if typecasting a longword to int64.

And this will ultimately give problems.

So the technique is not safe for general use and therefore it's a bad
example.

The following code will probably fail big time, I am not even going to test
it because I don't even use uint64's because of it's buggyness ! ;) =D

var
a : longword;
b : longword;
c : uint64;
begin
a := ...; // some large 32 bit value
b := ...; // some large 32 bit value
c := uint64(a) * uint64(b);
end;

Furthermore the uint64 type is bugged, last time I checked ! ;)
(Bug only shows up in large applications)

See my analysis website which divided into these problems with Delphi.

http://members.home.nl/hbthouppermans/D2007Analysis/

Perhaps Delphi XE has solved some of the problems... but don’t bet on it !
;) =D

Bye,
Skybuck.
Arivald
2011-06-24 10:14:04 UTC
Permalink
Post by Skybuck Flying
// *** begin of program ***
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
procedure test( p : Plongword );
begin
writeln( p^ );
end;
var
b : byte;
a : byte;
c : byte;
d : byte;
begin
try
b := 255;
a := 255;
c := 255;
d := 255;
No. Pointer conversion only.
Post by Skybuck Flying
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.
// *** end of program ***
I can vaguely remember other situations where using typecasts also
triggered all kinds of overflows.
There is no typecast in this code...
--
Arivald
Arivald
2011-06-24 10:09:06 UTC
Permalink
Post by Skybuck Flying
It works because of luck.
No. It woks, just works.
Post by Skybuck Flying
In other situation it will not work.
Make example... If You able.
Post by Skybuck Flying
Examples are fields in records and parameters on the stack.
Overflows will occur.
You wrong, this will work correctly.
Post by Skybuck Flying
Thus using typecasts like your example is a very bad programming
practice and should not be promoted by Delphi.
It is standard. Even compiler do such typecasts itself.
Post by Skybuck Flying
I also highly doubt your register explaination... I am pretty sure
Delphi is a 32 bit compiler and doesn't even know about 64 bit
registers, instead it has it's own 64 bit multiplication software.
It does not matter how it is implemented. It may be done using native
64-bit type, or two 32-bit registers, or using SSE. Does not matter.
What matters is fact that original variables are not changed, its values
are copied to temporary, wider places (in most cases some CPU registers)
before multiplication.
Post by Skybuck Flying
So please don't pretend you know anything about Delphi because you
clearly do not.
You clearly are moron... Dumb ass.
--
Arivald
Skybuck Flying
2011-06-24 15:11:24 UTC
Permalink
It's also not very usefull for address of operators.

@int64( a );

^ not allowed, so useless.

Also inspecting the value to which it is typecast is not possible ! ;) :)

Bye,
Skybuck.
Skybuck Flying
2011-06-24 15:12:54 UTC
Permalink
More fails:

move( int64(vValue), vBuffer, 8 );

Bye,
Skybuck.
Skybuck Flying
2011-06-24 15:16:13 UTC
Permalink
More fails:

move( int64(vValue), vBuffer, 8 );

^ This pretty much proves it's something very artificial and non-useable for
a lot of code.

There is no 64 bit memory region where the value is stored...

It's something "cocked-up" by the compiler.

In Delphi the concept of registers does not exist !

Therefore such int64 typecasting code is in Delphi/Pascal-sense bullshit !

Delphi only knows:

variables, records, classes, objects, etc.

Not registers ?!?!

Have you ever seen code like:

var
a : integer; register ?!?!?

Please direct me to documentation which claims "register" is part of the
Delphi language ! ;)

Please direct me to the documentation which claims "register" as a concept
part of the Delphi language.

Perhaps turbo pascal had such things, but those long gone.

The only thing close to this is "passing by register calling convention".

And that's pretty much it.

Bye,
Skybuck.
Skybuck Flying
2011-06-24 15:17:55 UTC
Permalink
One last concept which comes to mind is that of "temporarelies".

Which is what this is about.

It's save to say that the programmer may assume that all temporarelies
follow the rules of the language.

And int64 and 64 bit registers clearly do not.

Bye,
Skybuck.
Skybuck Flying
2011-06-24 15:26:50 UTC
Permalink
There could also be problems with overloaded procedures, perhaps even
properties, and also operator overloading, example:

overloaded_routine( int64(a) );

^ Which version will this call ?!?

I have seen cases where this would simply call the 32 bit version instead of
the 64 bit version ! ;)

These might have been compiler bugs, but you better check them out...

Could also be because of other parameters but still ! ;)

And there were definetly bugs with operator overloading ! Some of them have
been solved... but I have yet to test if all have been solved ! ;)

Bye,
Skybuck.
Skybuck Flying
2011-06-24 15:34:25 UTC
Permalink
Also there is something fundemantally deeply wrong with:

int64 := int32 * int32;

Not being able to produce an int64.

I suspect this is because the compiler is written in C and not Delphi or any
other language.

in C the result would simply wrap back.

However on the CPU the result is two 32 bit registers which is absolutely
logical otherwise multi-element multiplication could not be done.

General formula/rules/layout for multiplication should be:

Coverflow Csum = A * B;

A second location is needed to catch the overflow.

Just like:

A + B + CarryIn = Sum + CarryOut

(BorrowIn + A) - B = Difference + BorrowOut;

^ Something like that anyway... not gonna bother with correct formula's...
point is... multiple outputs required otherwise it's fubar-ed.

So it’s totally reasonable to expect int64 := int32 * int32; to perform a
decent 64 bit result ! ;)

Bye,
Skybuck =D
Skybuck Flying
2011-06-24 15:22:18 UTC
Permalink
Documentation of Delphi language is also fokked up in Delphi XE.

This could be a good reason for me to switch to something better documented
like c++ ! ;) :)

Bye,
Skybuck.
Skybuck Flying
2011-07-22 06:08:41 UTC
Permalink
"
As solution, cast one or both operands to wider type, example:

c := int64(a) * int64(b);
"

Another interesting question is:

What happens if this code is ported to another language say: C or C++.

Would C or C++ also "automatically convert this to 64 bit temporarelies" or
would this simply produce "memory overflows" ?!

In the last case you would be fokked if another noobie converts your code !
;) =D

Bye,
Skybuck.
Skybuck Flying
2011-07-22 06:29:33 UTC
Permalink
Ah/oh to bad.

I was hoping for some spectacular failure... then I had something to bitch
about ! ;) =D

But for now you escape my hook ! LOL.

But this doesn't mean that other languages are flawless too, there might
still be a case somewhere which will explode/fail spectacularly and when I
do find/encounter it someday... I will blast you the fok away with it ! ;)
=D

For now here is a test program for what it's worth ! ;) =D

// ** Begin of Test Program ***

// TestProgram.cpp : Defines the entry point for the console application.
//

/*

(C++) Test program to see what happens when C types are typecasted to larger
types.

So far Visual C/C++ Compiler seems to get off the hook and works ok.

This leaves question what Cuda C/C++ (nvcc.exe) would do ;)

Especially for char4 type ! ;)

*/

#include "stdafx.h"


int _tmain(int argc, _TCHAR* argv[])
{
int IntegerArray[4];
long long LargeInteger;

char CharArray[4];
float FloatArrayA[2];
float FloatArrayB[2];

// int Sentinel;

IntegerArray[0] = 0;
IntegerArray[1] = 0;
IntegerArray[2] = 0;
IntegerArray[3] = 0;

IntegerArray[0] = 5354432;
IntegerArray[2] = 2654762;

// Sentinel = 0;

LargeInteger = (long long)(IntegerArray[0]) * (long long)(IntegerArray[2]);

printf("IntegerArray[0]: %d\n", IntegerArray[0] );
printf("IntegerArray[1]: %d\n", IntegerArray[1] );
printf("IntegerArray[2]: %d\n", IntegerArray[2] );
printf("IntegerArray[3]: %d\n", IntegerArray[3] );

printf("LargeInteger: %lld \n", LargeInteger );

CharArray[0] = (char)255;
CharArray[1] = 124;
CharArray[2] = 63;
CharArray[3] = 14;

FloatArrayA[0] = (float) CharArray[0];
FloatArrayA[1] = (float) CharArray[1];

FloatArrayB[0] = (float) CharArray[2];
FloatArrayB[1] = (float) CharArray[3];

printf("FloatArrayA[0]: %f \n", FloatArrayA[0] );
printf("FloatArrayA[1]: %f \n", FloatArrayA[1] );
printf("FloatArrayB[0]: %f \n", FloatArrayB[0] );
printf("FloatArrayB[1]: %f \n", FloatArrayB[1] );

// printf("Sentinel: %d \n", Sentinel );

return 0;
}



// *** End of Test Program ***

Bye,
Skybuck.

Loading...