Discussion:
longint and/or overflow bug?
(too old to reply)
Jim Leonard
2012-10-30 15:56:34 UTC
Permalink
When I write and debug programs, it is always with range/stack/arith
checking on. In my current project, I am seeing Arith overflow {$Q+}
errors when trying to do legal operations with a longint variable as
the destination. Observe the following code:

===begin===
{$Q+,R+,S+}

const
val1=$0123; val2=$4567;

var
l:longint;
w1,w2:word;

begin
w1:=val1; w2:=val2;
l:=w1 * w2; {Throws runtime 215 "arithmetic overflow" if $Q+}
if l<>(val1*val2) then writeln('Error in longint math routines!');
end.
====end====

With $Q+, the second main line throws an overflow error even though
the source variables and values cannot possibly overflow the target.
With $Q-, the third line throws "error in longint math routines" as
the end result is wrong.

What is going on? Is there a bug in the longint math routines, or a
bug in code generation, or something else? If you run the above code
with your copy of TP7, do you see the same behavior?

(The workaround is to do everything "longhand" like this:

l:=w1;
l:=l * w2;

This works, but my code is starting to look ugly.)
Vinzent Höfler
2012-10-30 17:18:21 UTC
Permalink
Post by Jim Leonard
l:=w1 * w2; {Throws runtime 215 "arithmetic overflow" if $Q+}
Yes. The value is calculated in word-range, so it does actually
overflow. The type of the result variable does not matter at this
point.

You must tell the compiler to calculate this as longint. I'm not sure
if casting the whole expression to longint will suffice (probably
not), but casting at least one operand should tell the compiler to
use 32-bit arithmetic here.
Keep in mind that TP is a 16-bit compiler, it doesn't use 32-bit
arithmetic by default.


Vinzent.
Jim Leonard
2012-10-30 19:31:06 UTC
Permalink
On Oct 30, 12:18 pm, Vinzent Höfler
Post by Vinzent Höfler
  l:=w1 * w2; {Throws runtime 215 "arithmetic overflow" if $Q+}
Keep in mind that TP is a 16-bit compiler, it doesn't use 32-bit
arithmetic by default.
One would think that the compiler would be smart enough to output code
appropriate for the size of the destination -- I guess not! I'm
quite surprised I haven't encountered this before, and I've been
writing programs with TP7 on and off for 20 years.

I quickly tested casting one of the operands to longint() and that
worked, thanks for the suggestion.
r***@gmail.com
2012-10-30 21:50:54 UTC
Permalink
Hi,
Post by Jim Leonard
On Oct 30, 12:18 pm, Vinzent Höfler
  l:=w1 * w2; {Throws runtime 215 "arithmetic overflow" if $Q+}
I only can test TP 5.5, and it doesn't seem to support $Q+ at all.
Post by Jim Leonard
Keep in mind that TP is a 16-bit compiler, it doesn't use 32-bit arithmetic by default.
One would think that the compiler would be smart enough to output code
appropriate for the size of the destination -- I guess not! I'm
quite surprised I haven't encountered this before, and I've been
writing programs with TP7 on and off for 20 years.
I quickly tested casting one of the operands to
longint() and that worked, thanks for the suggestion.
Yeah, for TP 5.5, it works fine if w1 and w2 are both defined as "longint" but not if they are "word".

Keep in mind that the compiler probably isn't aware that your "word"s are small enough to not overflow at multiply, which may or may not be a bug. Probably in the interest of efficiency, it decided to always do "word" arithmetic in word registers instead of always using slower "longint" calculations on the rare chance that it might overflow. I guess it can't (easily?) figure out what's best by heuristics. (Perhaps on overflow it should promote to larger type, if available, and try again.) Some languages just use bigint all the time for simplicity [REXX, Scheme?], but that's obviously less efficient than fixed size (native) register types.

I'm not totally sure what the Pascal standard(s) say about signed vs. unsigned numbers, overflow, etc. Though official Pascal doesn't even have "longint" (32-bit signed) nor "word" (16-bit unsigned) as distinct types.

And 16-bit does complicate things a bit when trying to support 32-bit numbers, as I've noticed in various 16-bit compilers. So there are sometimes restrictions on array indices, "for" variables, what built-ins (e.g. INC) will support, etc. Even "case" expression sometimes must be short. It honestly depends on the compiler! (I'm mainly thinking of the following 16-bit: ACK, FST, Oberon-M.)

Modula-2 was more complicated than "standard" Pascal due to CARDINAL, but that was added because of native machine support of it and due to 16-bitness of the original CPUs, so it was more useful for addressing on those old machines. Both Modula-3 and Oberon simplified type compatibility a bit but in different ways. Modula-3 just made CARDINAL the positive subset of INTEGER; Oberon removed CARDINAL completely (as Ceres was 32-bit) but did have LONGINT and SHORTINT. Yet even that can cause complication and confusion in some obscure cases, so Wirth went ahead and removed them entirely from Oberon-07.

In short, it's hard to make an easy-to-use type system that works for 16-bit, 32-bit, 64-bit, etc. Sadly, most languages and implementations seem to assume too many things these days, esp. 32-bit or 64-bit or POSIX or Windows with Unicode and multi-threading, so it makes portability that much harder unless you go the easy (obvious) route of just using whatever's popular (i.e. not DOS).
Marco van de Voort
2012-10-31 09:35:02 UTC
Permalink
Post by Jim Leonard
var
l:longint;
w1,w2:word;
begin
w1:=val1; w2:=val2;
l:=w1 * w2; {Throws runtime 215 "arithmetic overflow" if $Q+}
typing:
longint :=word * word

Pascal rule is afaik that an expression is evaluated in (at least?) the
combined range of the operands. Which is word.

So the above translates to

tempresult: word = word * word
longint:=convert(word,longint,tempresult);

Note that FPC/Delphi do the same with 64-bit results (in 32-bit mode).
Marco van de Voort
2012-10-31 10:11:49 UTC
Permalink
Post by Marco van de Voort
Post by Jim Leonard
var
l:longint;
w1,w2:word;
begin
w1:=val1; w2:=val2;
l:=w1 * w2; {Throws runtime 215 "arithmetic overflow" if $Q+}
longint :=word * word
Pascal rule is afaik that an expression is evaluated in (at least?) the
combined range of the operands. Which is word.
Hmm, thinking of it, in Pascal the above is impossible, since range(word)
is not a subset of range(integer). Those are special yet again.

Still the behaviour is normal and consistent in the Borland lines and
compatibles.

Loading...