Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2013-11

subtract longwords with rollover

9 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Klaus Hartnegg

Hi,

How can I subtract two longwords such that when the result becomes negative, it rolls over?

A := A - B triggers runtime error 201 when the result is negative.

dec (A,B) rolls over, but triggers runtime error 201 when A is higher than the highest possible value of longint.

Is dec only declared for longint, but not for longword?

dec(longint(A),B) fails when the result is larger than 2 billion.

I cannot use longint instead of longword, this causes problems in other parts of the code.

HELP!

Thanks,
Klaus

Reply by email

Tomas Hajny
to Klaus Hartnegg

On Thu, November 28, 2013 17:37, Klaus Hartnegg wrote:

Hi,

Show quoted text 14 lines
How can I subtract two longwords such that when the result becomes negative, it rolls over? A := A - B triggers runtime error 201 when the result is negative. dec (A,B) rolls over, but triggers runtime error 201 when A is higher than the highest possible value of longint. Is dec only declared for longint, but not for longword? dec(longint(A),B) fails when the result is larger than 2 billion. I cannot use longint instead of longword, this causes problems in other parts of the code.

Both A := A - B fail only if you compile with range checking on and you try to assign a negative value to an unsigned type (which is obviously fully correct behaviour, because you indeed cross the defined range of the respective type). This implies several possible options:

1) Compile with range checking off (-Cr-). ;-)

2) Disable range checking for the respective part of your code (put {$RANGECHECKS OFF} and {$RANGECHECKS ON} around either "A := A - B" or "Dec (A, B)").

3) Perform an explicit typecast of your (negative) result to the type of
the target variable ("A := longword (A - B)").

Tomas

Reply by email

Klaus Hartnegg
to Klaus Hartnegg: How can I subtract two longwords such that when the result becomes negative, it rolls…

Sorry, I forgot to mention there is {$Q-} in the source.

The trick was indeed to use dec(longint(a),b). However I had missed one place in the code, so I got another runtime error from there.

But why does it only work with longint, and not with longword?

This looks like a missing declaration of one variant of the dec function.

var
   a,b : longword;
   i,j : byte;
begin
   {$Q-}
   for i := 0 to 4 do begin
      for j := 0 to 4 do begin
         write (i,' ',j,'  ');
         a := longword(j) * 1000000000;
         b := longword(i) * 1000000000;
         write (a:10,' ',b:10,' ');
       { a := a-b;  }         { fail at 1 0 }
       { dec (a,b); }         { fail at 1 0 }
         dec (longint(a),b);  { ok }
         writeln (a:11);
      end;
   end;
end.

Reply by email

Mark Morgan Lloyd
to Klaus Hartnegg: How can I subtract two longwords such that when the result becomes negative, it rolls…
Klaus Hartnegg wrote 16 lines
Hi, How can I subtract two longwords such that when the result becomes negative, it rolls over? A := A - B triggers runtime error 201 when the result is negative. dec (A,B) rolls over, but triggers runtime error 201 when A is higher than the highest possible value of longint. Is dec only declared for longint, but not for longword? dec(longint(A),B) fails when the result is larger than 2 billion. I cannot use longint instead of longword, this causes problems in other parts of the code.

What happens if you explicitly disable checking using $Q- $R-

Remember that converting $80000000 to base10 might be dismal.

Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues]

Reply by email

Tomas Hajny
to Klaus Hartnegg: Sorry, I forgot to mention there is {$Q-} in the source. The trick was indeed to use…
Klaus Hartnegg wrote
Sorry, I forgot to mention there is {$Q-} in the source.

I believe that $Q- covers overflow checks, not range checks.

Show quoted text 6 lines
The trick was indeed to use dec(longint(a),b). However I had missed one place in the code, so I got another runtime error from there. But why does it only work with longint, and not with longword? This looks like a missing declaration of one variant of the dec function.

.
.

Not really: it gives the same result if you use any other unsigned type too (byte, word) and the documentation for Dec() already tells you this explicitly ("A range check can occur, or an underflow error, if an attempt it made to decrease X below its minimum value."). (copied from the documentation sources including the typo - "it made" instead of "is made" ;-) )

It simply does as designed - if someone tries to fit a negative value into an unsigned variable, range checking detects it. You can avoid it by either performing an explicit typecast so that the result matches the variable type (so that the range is not crossed), or by disabling the range checking (locally for the respective part of code or globally).

Tomas

Reply by email

Sven Barth
to Tomas Hajny
"Tomas Hajny" wrote
> This looks like a missing declaration of one variant of the dec

function.

Aside from what Tomas said: there us no declaration of Dec(), because it is a compiler intrinsic and thus directly converted by the compiler into code.

Regards,
Sven

Reply by email

Klaus Hartnegg
to Tomas Hajny: I believe that $Q- covers overflow checks, not range checks. . . Not really: it gives the…
Tomas Hajny wrote
On Thu, November 28, 2013 18:12, Klaus Hartnegg wrote: > Sorry, I forgot to mention there is {$Q-} in the source. I believe that $Q- covers overflow checks, not range checks.

Thanks, this showed me the right direction.

Explanation: I'm trying to port code from Turbo Pascal. There word1-word2 does wrap around with {$Q-}{$R+}. And dec(word1) never checks for overflow, even with {$Q+}{$R+}.

FreePascal seems to have a wider definition of Range Check, and the inc() and dec() functions do check for overflow.

The longint trick prevents runtime error 201, but causes runtime error 215 if the longword value does not fit into longint.

On closer look this is all behaving like documented, just different from TurboPascal (maybe it would have worked in TP-compatibility-mode).

The correct fix is to simply add {$R-} to the code wherever it currently has {$Q-}. I have just done this, and now it passes the test.

Klaus

Reply by email

Jonas Maebe
to Klaus Hartnegg
Klaus Hartnegg wrote 17 lines
Explanation: I'm trying to port code from Turbo Pascal. There word1- word2 does wrap around with {$Q-}{$R+}. And dec(word1) never checks for overflow, even with {$Q+}{$R+}. FreePascal seems to have a wider definition of Range Check, and the inc() and dec() functions do check for overflow. The longint trick prevents runtime error 201, but causes runtime error 215 if the longword value does not fit into longint. On closer look this is all behaving like documented, just different from TurboPascal (maybe it would have worked in TP-compatibility- mode). The correct fix is to simply add {$R-} to the code wherever it currently has {$Q-}. I have just done this, and now it passes the test.

It's due to the fact that (then) Borland initially implemented range checking in an incomplete way, and didn't detect range errors that were caused by overflow in a register. They probably decided to add this to a separate check because just adding this functionality to {$R +} could break existing programs. However, semantically there is no difference between a range error and an overflow error: in both cases a value out of range is produced.

The reason that you sometimes get range errors in FPC while you would get overflow errors in TP, is because in Pascal by default all operations are carried out using the CPU native integer type. In TP this is 16 bit, in FPC 32 or 64 bit. As a result, several calculations that would overflow in TP become range errors in FPC.

Jonas

Reply by email

Tomas Hajny
to Klaus Hartnegg: Thanks, this showed me the right direction. Explanation: I'm trying to port code from…
Klaus Hartnegg wrote 14 lines
On 28.11.2013 18:32, Tomas Hajny wrote: > On Thu, November 28, 2013 18:12, Klaus Hartnegg wrote: >> Sorry, I forgot to mention there is {$Q-} in the source. > > I believe that $Q- covers overflow checks, not range checks. Thanks, this showed me the right direction. Explanation: I'm trying to port code from Turbo Pascal. There word1-word2 does wrap around with {$Q-}{$R+}. And dec(word1) never checks for overflow, even with {$Q+}{$R+}. FreePascal seems to have a wider definition of Range Check, and the inc() and dec() functions do check for overflow.

This is because TP is 16-bit (word), whereas FPC (in particular your compilation target in FPC) is 32-bit (longword).

Show quoted text 5 lines
The longint trick prevents runtime error 201, but causes runtime error 215 if the longword value does not fit into longint. On closer look this is all behaving like documented, just different from TurboPascal (maybe it would have worked in TP-compatibility-mode).
{$MODE TP} wouldn't change this. Only the i8086-msdos target (16-bit)
should behave the same way as TP from this point of view.
The correct fix is to simply add {$R-} to the code wherever it currently has {$Q-}. I have just done this, and now it passes the test.
That was indeed one of my proposed solutions ({$R-} is equivalent to
{$OVERFLOWCHECKS OFF}).

Tomas

Reply by email