There appears to be no LONGCARD in ISO Modula-2.
Does anyone know why not?
Sincerely,
john
value : CARDINAL [0..65535]
the compiler can work out that it only needs 16 bits to store this, i.e.
2 LOCs on a byte addressed machine.
The advantage of this approach is that it makes software more portable -
for example, if you wrote a program defining a variable as
value : CARDINAL
that relied on CARDINAL being 32 bits, then ported it to a different
machine or different compiler where CARDINAL was only 16 bits, your
program would still compile, but would not work correctly, whereas
if you defined it as the appropriate subrange, the program would fail
to compile.
The disadvantage of this approach is that it provides no way of
specifying the precision of arithmetic operations - it assumes all
operations are carried out at full precision.
Martin
There is just one area where I'd like to have a LONGCARD. If A and B
are of type CARDINAL (of whatever range you care to specify), then
_logically_ the type of A*B should be an unsigned integer occupying
twice the number of bits. (With a matching switch back for a division.)
Hardware designers have known this for decades, but you hardly ever
see software that takes advantage of the hardware. At various times
I have discovered that a calculation that is elementary in assembly
language becomes fiendishly difficult in any high-level language.
Note that, more often than not, the result of the A*B is a temporary
value in the middle of a calculation. Programmers mightn't even have
to know about the different types of the intermediate variables, other
than knowing that there is an automatic type conversion - with
appropriate overflow checking - at the point where the assignment
is made.
Obviously what I'm suggesting can't be implemented in any existing
language, except to the extent that it can be done with library
functions (something that I have in fact done for my own use),
because it would break the existing type compatibility rules.
I offer it, however, for the consideration of any designers of
future languages.
--
Peter Moylan peter at ee dot newcastle dot edu dot au
http://eepjm.newcastle.edu.au (OS/2 and eCS information and software)
Most hardware has supported the multiply to generate a result twice the
size. Much of this has been because the the processors were "small". Like 8
or 16-bit and larger procision numbers were desirable for many things. The
processor operator was supporting multi-precision arithmetic. 32-bit
processors mostly acted basically the same. At 64-bit the line is being
drawn. SPARC for example does not generate a 128-bit result for its 64-bit
multiply. PowerPC has never supported this. It has two instructions. One to
generate the normal "low" multiply and another to generate the "high"
multiply. Of course since AMD64 is a simple extension to 64-bit from 32-bit
just like the extension from 16 to 32-bit, this multiply does result in a
128-bit result. No choice here as your hands are tied.
There is no _logical_ reason for A*B to result in double bits unless you are
doing multiple precision arithmetic. This is the exception and not the rule.
If it is a logical argument than A+B should result in double bits as well.
Both can overflow. In fact having A*B result in double bits slows the
instruction down. This is the reason PowerPC has two instructions. For
normal use where you only want a multiply you get full speed. In the other
circumstance then you use the second instruction. A*B is a common operation
for compilers when accessing arrays where the elements are not a power of 2
in size. You want a fast multiply. Of course you can have a fast multiply at
any bits size. The cost is simply a lot more transisitors.
Our compiler, and the MSC compiler, both detect when code is doing an A*B
and actually wanted the double bits result and if the processor supported it
used the appropriate instruction. Same for add/subtract.
This is an excerpt from our VLI (very large integer) module which supports
arbitrary precision arithmetic.
787 PROCEDURE MultiplyDigitAdd(ptrA : DigitsPointer;
788 digitB : Digit;
789 ptrR : DigitsPointer;
790 count : CARDINAL;
791 assign : BOOLEAN) : Digit;
792 VAR
793 temp : Digit2;
794 carry : Digit;
795 BEGIN
0300 P34
0300 55 PUSH EBP
0301 89 E5 MOV EBP, ESP
0303 53 PUSH EBX
0304 56 PUSH ESI
0305 PROLOG
796 (* writing the code ugly (pointer arithmetic) causes simpler
addressing
797 to be used for memory ops.
798 ugly is okay for a performance critical loop.
799 *)
800 carry := 0;
0305 31 DB XOR EBX, EBX
0307 90 ALIGN08(8)
801 LOOP
0308 L0003
802 IF count >= 8 THEN
0308 83 7D 0C 08 CMP DWORD PTR [EBP+0C], 00000008(8)
030C 0F 82 000000D5 JB NEAR L0006
803 count := count - 8;
0312 83 6D 0C 08 SUB DWORD PTR [EBP+0C], 00000008(8)
804
805 temp := (VAL(Digit2, digitB) * VAL(Digit2, ptrA^[0])) +
0316 8B 01 MOV EAX, DWORD PTR [ECX]
0318 F7 65 10 MUL DWORD PTR [EBP+10]
031B 03 07 ADD EAX, DWORD PTR [EDI]
031D 83 D2 00 ADC EDX, 00000000(0)
0320 01 D8 ADD EAX, EBX
0322 83 D2 00 ADC EDX, 00000000(0)
806 VAL(Digit2, ptrR^[0]) +
807 VAL(Digit2, carry);
808 <*/PUSH/NOCHECK:A*>
809 ptrR^[0] := temp;
0325 89 07 MOV DWORD PTR [EDI], EAX
810 <*/POP*>
811 carry := temp SHR DigitBits;
812
813 temp := (VAL(Digit2, digitB) * VAL(Digit2, ptrA^[1])) +
0327 8B 41 04 MOV EAX, DWORD PTR [ECX+4]
032A 89 D3 MOV EBX, EDX
032C 31 D2 XOR EDX, EDX
032E F7 65 10 MUL DWORD PTR [EBP+10]
0331 03 47 04 ADD EAX, DWORD PTR [EDI+4]
0334 83 D2 00 ADC EDX, 00000000(0)
0337 01 D8 ADD EAX, EBX
0339 83 D2 00 ADC EDX, 00000000(0)
814 VAL(Digit2, ptrR^[1]) +
815 VAL(Digit2, carry);
816 <*/PUSH/NOCHECK:A*>
817 ptrR^[1] := temp;
033C 89 47 04 MOV DWORD PTR [EDI+4], EAX
818 <*/POP*>
819 carry := temp SHR DigitBits;
Since Modula-2 uses homogeneous expressions (i.e. does not perform automatic
type promotion) you notice that the single precision integers are converted
to double precision and then multiplied. The compiler detects this and skips
the conversion and lets the single precision multiply result in double bits.
Also the adds add a sigle precision value to the double precision "temp"
computation. A language like C or Pascal with automatic type conversion
would not need such explicit conversions. It should be noted that those
languages still do the same things as M2. It is just that M2 makes the
conversions apparent in the source code since they are not done under the
covers.
Norman