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.
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.
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.
> 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.
>
> 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
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.
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".
>
> ^ Allowing such typecasts is nonsense.
What? Casting to wider type is always safe.
>
> 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..
>
> I cannot understand why such code would suddenly work ;)
I see... Why I'm not surprised?
--
Arivald
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.
// *** 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 :)
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.
In article <d2616$4e03734d$5419acc3$14...@cache5.tilbu1.nb.home.nl>,
Window...@DreamPC2006.com says...
>
> 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.
True, but if you use them properly rather than blindly, then there's no
trouble. Typecasts are standard in nearly all languages.
No. It woks, just works.
> In other situation it will not work.
Make example... If You able.
> Examples are fields in records and parameters on the stack.
>
> Overflows will occur.
You wrong, this will work correctly.
> 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.
> 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.
>
> So please don't pretend you know anything about Delphi because you
> clearly do not.
You clearly are moron... Dumb ass.
--
Arivald
No. Pointer conversion only.
> 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
Moron... whee in your example You cast to wider type with error? Only
one error is on casting $FFFFFFFF to integer.
>
> 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
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.
@int64( a );
^ not allowed, so useless.
Also inspecting the value to which it is typecast is not possible ! ;) :)
Bye,
Skybuck.
move( int64(vValue), vBuffer, 8 );
Bye,
Skybuck.
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.
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.
This could be a good reason for me to switch to something better documented
like c++ ! ;) :)
Bye,
Skybuck.
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.
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
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.
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.