March 1999 Draft
JavaScript 2.0
Machine Types Library
|
Tuesday, March 23, 1999
The machine types library is an optional library that provides additional low-level types for use in JavaScript 2.0 programs.
On implementations that support this library, these types provide faster, Java-style integer operations that are useful for
communicating between JavaScript 2.0 and other programming languages and for performance-critical code. These types are not
intended to replace number
and integer
for general-purpose scripting.
When the machine types library is imported via an import of "machine-types"
version 1, the following types
become available:
Type |
Values |
---|---|
byte |
Machine integers between -128 and 127 inclusive |
ubyte |
Machine integers between 0 and 255 inclusive |
short |
Machine integers between -32768 and 32767 inclusive |
ushort |
Machine integers between 0 and 65535 inclusive |
int |
Machine integers between -2147483648 and 2147483647 inclusive |
uint |
Machine integers between 0 and 4294967295 inclusive |
long |
Machine integers between -9223372036854775808 and 9223372036854775807 inclusive |
ulong |
Machine integers between 0 and 18446744073709551615 inclusive |
Values belonging to the eight machine integer types above are distinct from each other and from values of type integer
.
Thus, byte(7)
is distinct from int(7)
, which in turn is distinct from the plain integer 7
.
However, the coercions listed below usually hide these distinctions.
No subtype relations hold between the machine types.
The above type names are not reserved words.
The following coercions take place:
integer
value v can be coerced to one of the machine integer types M
if v is within range of the target type M. Both +0
and -0
coerce to the
machine integer 0. Note that non-integer numbers are not coerced to any of the machine types.byte
| =
|ubyte
| = 256, |short
| = |ushort
| = 65536, |int
| = |uint
|
= 232, and |long
| = |ulong
| = 264.integer
or number
as long as m
can be represented exactly using the IEEE double-precision floating-point format. 0 always becomes +0
.Machine integers support the arithmetic operators +
, -
, *
, /
, %
,
comparisons ==
, !=
, <
, >
, <=
, =>
,
and bitwise logical operations ~
, &
, |
, ^
, <<
,
>>
. If supplied two operands of different machine integer types M1
and M2, all of these binary operators except <<
and >>
first coerce both operands to the same type M. If M1 appears before M2
in the list byte
, ubyte
, short
, ushort
, int
, uint
,
long
, ulong
, then M is M2; otherwise M
is M1. Then these operators perform the operation and finally return the result as a
value of type M. If the result is not within range of the target type M, it is treated modulo |M|.
If one of the operands is a machine integer of type M and the other is an integer
value v,
then v is first coerced to type M.
The result type of a shift expression (<<
or >>
) is the same as the type of its first
operand. The second operand's type does not affect the type of the result. Right shifts are signed if the first operand has
type byte
, short
, int
, or long
, and unsigned if it has type ubyte
,
ushort
, uint
, or ulong
.
These rules are designed to permit machine integer operations to be implemented as single instructions on most processor
architectures yet give predictable results. Overflows wrap around instead of signaling errors because such behavior is useful
for many bit-manipulation algorithms and permits much better optimization of performance-critical code. Code that is concerned
about overflows should be using regular integer
instead of the machine integer types.
Why are values of the eight machine integer types distinct? This was done because of a desire to allow arithmetic operators
to only support 32 bits when operating on int
values. Let's take a look at the alternative:
Suppose we unify the values of all eight machine types so that int(2000000000)
is indistinguishable from
long(2000000000)
. To what precision should an operator like +
calculate its results? Clearly,
if we're adding two long
values and the result is within the range of long
values, then we'd
expect to get the right result. In particular, long(2000000000)
+
long(2000000000)
should yield long(4000000000)
. However, long(2000000000)
is indistinguishable from int(2000000000)
,
so int(2000000000)
+
int(2000000000)
should also yield long(4000000000)
,
which is not representable as an int
value. Thus, even if both operands are known to be int
values, the +
operator has to use 64-bit arithmetic.
If a
has type int
and we compute a+1
, then we have to use 64-bit arithmetic
because the result could be 2147483648. However, if we compute var int r = a+1
instead, then a smart compiler
could make do with 32-bit arithmetic because the result is treated modulo 232. However, this trick would not
work with an expression such as var boolean b = a+1 > 0
.
The alternative is viable but it leads to more demand for 64-bit arithmetic. It does have the advantage that one does not need to worry about intermediate overflows as long as the values don't approach 264.
Do we want to support a float
type for holding single-precision IEEE floating-point numbers? This type may
be useful for:
float
s originally written in another language such as C++ or Java that one would want to
replicate exactly in JavaScript; without support for the float
type the JavaScript version would give different
answers from the original.One difficulty with supporting float
is deciding what the coercion rules should be. If we invoke +
with one number
operand and one float
operand, should the result be a float
or a
number
? One might expect number
, but this makes adding constants to float
s using
single-precision arithmetic awkward since every constant is a number
. If s
is a float
,
the expression s+1
would yield a number
instead of a float
because 1
is a number
. One would have to write s+float(1)
instead.
Waldemar Horwat Last modified Tuesday, March 23, 1999 |