[M3devel] cm3 regression

Rodney M. Bates rodney.bates at wichita.edu
Wed Apr 16 22:40:57 CEST 2008



Jay wrote:
>>>it'd be nice imho for    the language or m3core to also build-in UINTEGER, INT8, INT16,
>>>INT32, INT64, UINT8, UINT16, UINT32, UINT64, just so folks wouldn't   
>>>have to provide them themselves. (CARDINAL I don't think is what I   
>>>want.)
>>>Add them somewhere in m3core?
>>>MODULE Integers; ?
>>
>>Such things may be convenient for programmers, but are left out of the
>>language because the specification must not become too long, I think.
>>I'd opt for library extensions in this case.
> 
> 
> Good enough.
> Except I'm not sure about UINTEGER..like..why is CARDINAL only half range?
> I'd have to dig in..NOT now..
> 

I've been through this in Modula-2.  There, CARDINAL is a whole, unsigned
range.  Unfortunately, this created a linguistic and portability nightmare.
It was worse because I don't think the original Modula-2 definition completely
specified things like assignability between the two, operators on mixtures
of the two, implied conversions, etc.  I know different implementations were
different to the point it was difficult even to write code that would compile
on them all.

Maybe there is a cleaner way to have two distinct types, one signed, one
unsigned, and both full range, but it would at least be tricky.  The Modula-3
way is found in Word.  Word.T is the same type as INTEGER, but the operations
in Word (e.g. Word.Times) apply unsigned interpretation to the bits of the
same-typed value.  This avoids a lot of problems.  So for full range, unsigned,
values, use Word (and probably, to show your intent to readers, declare
variables as Word.T rather than INTEGER, even though it is the same type).

As for CARDINAL in Modula-3, it was originally exactly [0..LAST(INTEGER)].
This was changed later to say it is "just like" [0..LAST(INTEGER)].
It was years before I fully understood this.  I once posted a question on
the M3 newsgroup about it and gave up waiting for an answer.  Then I read
the compiler code and found what it means, literally, is that CARDINAL is a
different type from [0..LAST(INTEGER)], but the two have all the same legal operations.
This distinction seldom matters, because it is almost always assignability and
not type equality that is relevant.  Which didn't help explain why.

Then there was an answer to my question saying that the change in the definition
of CARDINAL was to support Pickles, but this still left me in the dark.

Finally, while working on Pickles, it dawned on me that, without the change,
it would not be possible for Pickles to change the size of a CARDINAL between
32 & 64 bits.  The type [0..LAST(INTEGER)] (or any other similar subrange) is
a different type if LAST(INTEGER) has a different value.  CARDINAL, in contrast,
can change range between platforms and still be the same type.

Moral: Don't pickle anything whose type contains values like LAST(INTEGER) that
differ from platform to platform.

> 
>>Yes, IIRC there are no checks on integer overflow; this is an
>>implementation flaw. How would you provide this? Add generation of
>>checks on every integer operation?
> 
> 
> Well, it's a no-win situation.
> The check would bloat the code.
> Even function calls would bloat and slow.
> Probably function calls.

I happen to be extremely conservative on the value of detecting runtime errors,
even at efficiency cost.  I heard some horror stories in the early years of my
career about programs that had been in use for years and whose output had been
used to plan the expenditure of huge amounts of money, then were accidentally
found to be producing wrong (though not obviously so) output, that would have
been caught by running with runtime checks enabled.

In any case, big time cost increases (e.g. double) associated with a few operations
very often amortize over the whole program's run time to only 1% or 2%.

> 
> And then hopefully optimize them some.
> For example:
> FOR i := 0 TO n DO
>    ...
> END;
> 
> Check up front then n isn't negative, and then no checking is needed for the incrementing of n to overflow.

Overflow in compiler-generated arithmetic is a little different than in arithmetic explicitly
coded by the programmer.  In this example,  the language specifically exempts the compiler from
worrying about this.  2.3.16, last sentence:  "If the upper bound of the loop is LAST(INTEGER),
it should be rewritten as a WHILE loop to avoid overflow."

As for other optimizations, the language specifies the semantics, so a compiler is free to do
anything that complies with this.

> 
> Similarly,
> FOR i := 0 TO n BY 2 DO
> 
>    ...
> 
> END;
> 
> or whatever is the syntax, check if the start/end are divisible by the increment and the start is less than the end, and then no overflow check needed

-- 
-------------------------------------------------------------
Rodney M. Bates, retired assistant professor
Dept. of Computer Science, Wichita State University
Wichita, KS 67260-0083
316-978-3922
rodney.bates at wichita.edu



More information about the M3devel mailing list