[fpc-devel] bug: Inc(v,i)/Dec(v,i)

Hans-Peter Diettrich DrDiettrich at compuserve.de
Sun Jul 10 13:14:47 CEST 2005

Marcel Martin wrote:

> > Why not? I expect that the compiler evaluates constant expressions, and
> > creates the best code for the resulting values.
> The best code is, before all, the correct code.

So we have to find out what is *correct*.

> > In the case of Inc(x, SomeConstant) the value of the named constant can
> > be changed, with arbitrary positive or negative values. Would you then
> > want to find all uses of "someconst" in your code, to find out where
> > your code deserves a modification?
> If I declare a constant as an unsigned integer, if I wrote my
> code assuming it is an unsigned integer and if, suddenly,
> I decide to change that, of course, I may expect some problems.

Why should one expect problems when incrementing or decrementing an
value, maybe signed or unsigned?

> Note: I have the feeling that you are confusing signed/unsigned
> and negative/positive. When I talk of unsigned integers, I am not
> talking about signed ones that may be positive. No, I am talking
> about integers that can never be negative.

I do not confuse anything like that.

> > IMO every calculation, that can result in illegal results in *normal*
> > operation, has to be handled appropriately in the code. If no problems
> > are predicted, expected, and handled, in explicit code, the compiler
> > only has to care about coding errors, that result in *unexpected*
> > overflows. I don't think that a calculation should produce or not
> > produce an overflow, depending only on the sign of the given value.
> Unexpected overflows? The overflows occur or not according to
> simple rules, perfectly defined, which ones depend on the types
> of the operands.

Unexpected as depending on the values, not on the types. When the ranges
of the possible operands are known, it's also known whether an overflow
can occur in computations with these operands. 

> If A is a Longword (this is an unsigned type, no instance can be
> negative). So if A = 2^32-1 and if I compute A + 1, there is an
> overflow because the resulting value should be 2^32 and this value
> doesn't exist for the Longword type.

No doubt, when the compiler doesn't extend the range of the arguments
(see below).

>  > From
> > the mathematical viewpoint +(+1) and -(-1) is perfectly equivalent, as
> > is +(-1) and -(+1).
> Computationally, this is not equivalent.
> +(-1) -> add eax, $ffffffff
> -(+1) -> sub eax, $00000001
> And at this point, even if we know the content of eax before the
> operation, we cannot know if there is or not an overflow without
> knowing the type of the integer contained in eax.

Since the hardware cannot know whether $ffffffff is a huge unsigned
number, or a small signed number, a possibly thrown or indicated
overflow from above instructions is meaningless. This is why a proper
and unoptimized implementation must extend values of different
signedness to the next bigger type, so that the calculation cannot
produce an overflow.

When you consult Wirth and the design of Pascal and his following
languages, you'll find out that unsigned data types always are subranges
of bigger signed data types. This is why we cannot have an proper UInt64
type without an proper Int128 type, unless one wants to break all sane
design rules.

> Moreover, when programming, mathematical considerations have to
> be handled with care. Mathematically, (x = -x) -> (x = 0).
> Computationally, this is wrong (because the computer doesn't work
> over the ring Z but over a ring Z/2^kZ). If x is a Longint and
> if x = -x, either x = 0 or x = -2^31.

Bad example. In 2's complement both 0 and -2^31 have no counterpart of
the opposite sign, whereas in 2-1 complement (sorry, don't know the
English term) the negative values are represented by the bitwise
complement of the positive values. When there exists no representation
of -(-2^31), you cannot perform an comparison with that value.

> Try this
>    x : Longint;
>    ...
>    x := Longint($80000000); // x := -2^31
>    WriteLn(IntToStr(x));
>    x := -x;
>    WriteLn(IntToStr(x));
> (And this is not a bug. This is a consequence of the 2-complement
> representation of signed integers.)

It is a bug. As outlined before, the above assignment of x:=-x should
raise an range check error. A comparison of x=-x will return False, in
the Int64 range.

The expression Longint($80000000) is questionable because, in the strict
sense, it forces the compiler to perform an illegal operation, which
only happens to produce an in-range value on specific machines.

I know that unsigned values frequrently are not appropriately handled by
many compilers, for *performance* reasons. I also know that many
external routines use unsigned values in an inappropriate way, only
because it's easier to define an "unsigned int" instead of an proper
subrange, and because many languages do not support subrange types at
all. I agree that we must live with such weird declarations, but there
exists no need, besides performance, for handling such datatypes in an
inappropriate way. When you rethink all your arguments and examples,
based on an compiler that maps unsigned data types into subranges of the
next higher signed type, you'll find out that all problems go away.
Every "optimization", violating that rule, will cause selfmade trouble


More information about the fpc-devel mailing list