Board index » delphi » LongInt*Word+Word=Arithmetic Overflow?

LongInt*Word+Word=Arithmetic Overflow?

Hi all!
When I was writing some graphicroutines for SVGA i've suddenly had
a problem which wasn't shown because I hadn't turned the Overflow check
on. In the little program occurs on my machine(Cyrix 686 P166, BP7) a
Arithmetic Overflow. Can anyone of you explain WHY this program doesn't
work?

{$Q+}
program Bug_Test;
var
  l : LongInt;
  x,y : Word;
begin
  x:=639;
  y:=479;
  l:=y*640+x;
end.

 

Re:LongInt*Word+Word=Arithmetic Overflow?


Hi!

In your example, you do the following:
Multiply two words, and add another.
The result is also a word type but does not fit into 16 bits.

You can tell the compiler to use longint type:

  l:=y*longint(640)+x;

This fixes your problem.

Andras

Re:LongInt*Word+Word=Arithmetic Overflow?


Quote
Martin Beck wrote:

> Hi all!
> When I was writing some graphicroutines for SVGA i've suddenly had
> a problem which wasn't shown because I hadn't turned the Overflow check
> on. In the little program occurs on my machine(Cyrix 686 P166, BP7) a
> Arithmetic Overflow. Can anyone of you explain WHY this program doesn't
> work?

> {$Q+}
> program Bug_Test;
> var
>   l : LongInt;
>   x,y : Word;
> begin
>   x:=639;
>   y:=479;
>   l:=y*640+x;
> end.

  The reason for the overflow is that when the computer calculates the
y*640+x statement, it tries to do it as a WORD, not a longint. As (479 *
640) + 639 is well above the word limit (65535), it produces an
overflow. There are two main ways to solve this :

  Firstly, you can define y as a longint instead of a word. This will
make the computer calculate the expression as a longint, and will not
overflow.

  Secondly, you can type-cast* y as a longint in the equation :

  l := (longint(y) * 640) + x;

  This should also work. I'm pretty sure that TP7 supports this (i'm not
sure - i've never used TP7), but i don't think TP6 does.

  *NOTE : I realise that type-casting is not the correct name for this -
as someone pointed out to me in a previous message :) - but i can't for
the life of me remember it's correct name, and the message that it was
in has been removed. So, please don't flame me too much. :)

        - Bob

Re:LongInt*Word+Word=Arithmetic Overflow?


Quote
On Mon, 10 Aug 1998 20:59:23 +1000, Bob <mich...@unsw.edu.au> wrote:

>  Secondly, you can type-cast* y as a longint in the equation :

>  l := (longint(y) * 640) + x;

>  This should also work. I'm pretty sure that TP7 supports this (i'm not
>sure - i've never used TP7), but i don't think TP6 does.

TP 7.0 does and TP 6.0 downto 4.0 do support it...

Quote
>  *NOTE : I realise that type-casting is not the correct name for this -
>as someone pointed out to me in a previous message :) - but i can't for
>the life of me remember it's correct name, and the message that it was
>in has been removed. So, please don't flame me too much. :)

The "correct" name is a purely academic question and there is not the
least reason to flame.  Borland's docs call it a typecast and the
terminology of other programming language Standards like C and C++
call it 'cast', too.

The name "cast" applies to the function-like syntax

        newttype(...)

 - although behind the application of this syntax may be hidden two
semantically different effects in TP. Either a conversion of a value
or a reinterpretation of a reference to an object.

A "value cast" may be applied to an ordinal or pointer expression -
the target type may be any ordinal or pointer type. The value may be
truncated

        byte(word_value)          ( $FFFF -> $FF )

or enlarged

        longint(word_value)          ( $FFFF -> $0000FFFF )
        longint(integer_value)       ( -1 = $FFFF -> $FFFFFFFF = -1 )

or just "reinterpreted"

        shortint(byte_value)      ( 255 -> -1 )

A "variable cast" may be applied to any data reference in memory -
provided that the target type has exactly the same size as the source
type. It tells the compiler to reinterpret the variable reference as a
reference to the target type.

type ra = array[0..5] of byte;
var r:real;

r:=2;

ra(r)[0]:=ra(r)[0]+3;     ( Performs r:=r*8 )

Here "ra(r)" behaves like a variable of type ra. It works like a
declaration

var
  rr:ra absolute r;

"on the fly".

Note that

        ra(1.0)[0]:=2;

is illegal because 1.0 is not a memory reference but a value.

By the way, there is another not so well known bug in in the TP 7.0
compiler which is corrected in the TP 7.01 update and does not occur
in TP 6.0-.

var
  a:array[0..99] of byte;
  b:byte;
begin
 a[0]:=0; a[1]:=1; a[2]:=0; a[3]:=0;
 writeln(longint(ord(a[0]))+1);
end.

With TP 7.0 the result will be 257 which is incorrect, of course. The
longint cast to ord(byte_var) will erroneously interpret the parameter
of ord() as a longint _variable_.

Of course this construction is redundant anyway.

        ord(a[0])+1

and

        longint(a[0]+1)

would yield the correct result 1.

Regards
Horst

Re:LongInt*Word+Word=Arithmetic Overflow?


In article <6qkcba$14...@news02.btx.dtag.de>, Martin Beck <Spix_1@t-
online.de> writes
Quote
> Can anyone of you explain WHY this program doesn't
>work?

>{$Q+}
>program Bug_Test;
>var
>  l : LongInt;
>  x,y : Word;
>begin
>  x:=639;
>  y:=479;
>  l:=y*640+x;
>end.

The result of the calculation is only converted to longint and the *end*
of the calculation and 479*640 is larger than the maximum size of a
word. You must force the whole calculation to be longint using:

  l:=longint(y)*640+x;

--
Pedt Scragg                    <newsmas...@pedt.demon.co.uk>

Never curse the Crocodile's mother before crossing the river

Re:LongInt*Word+Word=Arithmetic Overflow?


In article <6qkcba$14...@news02.btx.dtag.de>,

Quote
Martin Beck <Spi...@t-online.de> wrote:
>Hi all!
>When I was writing some graphicroutines for SVGA i've suddenly had
>a problem which wasn't shown because I hadn't turned the Overflow check
>on. In the little program occurs on my machine(Cyrix 686 P166, BP7) a
>Arithmetic Overflow. Can anyone of you explain WHY this program doesn't
>work?

>{$Q+}
>program Bug_Test;
>var
>  l : LongInt;
>  x,y : Word;
>begin
>  x:=639;
>  y:=479;
>  l:=y*640+x;

It is the y*640. That is a word multiplication and if will result to
306560 which does not fit in a word. TP does not know that you want
longint result. The  fact that you assign the result in a longint is
irrelevant.

What you need to do is to typecast either y or 640 as longint:

l:=longint(y)*640+x;

This will make the multiply as longint and also the subsequent addition
as the result of the multiplication is longint. (if either operand is
longint the operation is longint).

The drawback of above is that it generates a subroutine call. For this
reason I have created following inline function:

Function Wmul(x,y:word):longint;
  inline(
          $5B/              {POP     BX}
          $58/              {POP     AX}
          $F7/$E3           {MUL     BX}
         );

You use it like this: l:=wmul(y,640)+x;

Lets get a bit deeper and view the ASM produced by your code with no
error checking and 8088 code generation:

BUG_TEST.10:  l:=y*640+x;
  cs:001B B88002         mov    ax,0280
  cs:001E F7265600       mul    word ptr [BUG_TEST.Y]
  cs:0022 03065400       add    ax,[BUG_TEST.X]
  cs:0026 31D2           xor    dx,dx
  cs:0028 A35000         mov    [BUG_TEST.L],ax
  cs:002B 89165200       mov    [0052],dx

See how the addition is done on 16-bit and DX is cleared before the
assignment. The change to make _this_ code work OK is trivially small.
One would only have to change the "xor dx,dx" to "adc dx,0"

However, to make the code work in pure Pascal one needs to do the
typecast and the result will be:

BUG_TEST.10:  l:=longint(y)*640+x;
  cs:001B A15600         mov    ax,[BUG_TEST.Y]
  cs:001E 31D2           xor    dx,dx
  cs:0020 B98002         mov    cx,0280
  cs:0023 31DB           xor    bx,bx
  cs:0025 9A9904AD5F     call   5FAD:0499  ; dx:ax:=dx:ax*cx:bx
  cs:002A 8BC8           mov    cx,ax
  cs:002C 8BDA           mov    bx,dx
  cs:002E A15400         mov    ax,[BUG_TEST.X]
  cs:0031 31D2           xor    dx,dx
  cs:0033 03C1           add    ax,cx
  cs:0035 13D3           adc    dx,bx
  cs:0037 A35000         mov    [BUG_TEST.L],ax
  cs:003A 89165200       mov    [0052],dx

My wmul produces following code:

BUG_TEST.14:  l:=wmul(y,640)+x;
  cs:001B FF365600       push   word ptr [BUG_TEST.Y]
  cs:001F B88002         mov    ax,0280
  cs:0022 50             push   ax
  cs:0023 5B             pop    bx
  cs:0024 58             pop    ax
  cs:0025 F7E3           mul    bx
  cs:0027 8BC8           mov    cx,ax
  cs:0029 8BDA           mov    bx,dx
  cs:002B A15400         mov    ax,[BUG_TEST.X]
  cs:002E 31D2           xor    dx,dx
  cs:0030 03C1           add    ax,cx
  cs:0032 13D3           adc    dx,bx
  cs:0034 A35000         mov    [BUG_TEST.L],ax
  cs:0037 89165200       mov    [0052],dx

Note this does not apply to bytes/words. If you do calculation on bytes
they are automatically converted as words. This is because TP is a
16-bit compiler and there is no penalty on doing 16-bits instead of 8.
However, there is a large penalty on doing 32-bits instead of 16 so you
must explicitly tell when you want 32-bits.

A similar function can be used of the multiplication is followed by
division:

Function WmulDiv(x,y,z:word):word;
  inline(
          $59/             {POP     CX}
          $5B/             {POP     BX}
          $58/             {POP     AX}
          $F7/$E3/         {MUL     BX}
          $F7/$F1          {DIV     CX}
         );

This is practical in various kind of scalings. Note that the result is
word but the intermediate result can freely overflow to 32 bits.

Quote
>end.

Osmo

Other Threads