July 2000 Draft
JavaScript 2.0
Libraries
Machine Types
|
Wednesday, February 16, 2000
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 MachineTypes
version 1, the following types become
available:
Type |
Unit |
Values |
---|---|---|
byte |
B |
Machine integers between -128 and 127 inclusive |
ubyte |
UB |
Machine integers between 0 and 255 inclusive |
short |
S |
Machine integers between -32768 and 32767 inclusive |
ushort |
US |
Machine integers between 0 and 65535 inclusive |
int |
I |
Machine integers between -2147483648 and 2147483647 inclusive |
uint |
UI |
Machine integers between 0 and 4294967295 inclusive |
long |
L |
Machine integers between -9223372036854775808 and 9223372036854775807 inclusive |
ulong |
UL |
Machine integers between 0 and 18446744073709551615 inclusive |
float |
F |
Single-precision IEEE floating-point numbers, including positive and negative zeroes, infinities, and NaN |
Values belonging to the nine machine types above are distinct from each other and from values of type integer
.
A literal may be written by using one of the units provided: 7B
is the same as byte(7)
, which is
distinct from 7I
, which in turn is distinct from the plain integer 7
. A float
NaN is
distinct from the regular Number
NaN. However, the coercions listed below often hide these distinctions.
No subtype relations hold between the machine types.
The above type names are not reserved words.
The units are defined using the standard unit facility. They may be overridden by the user.
The following coercions take place:
byte
| =
|ubyte
| = 256, |short
| = |ushort
| = 65536, |int
| = |uint
|
= 232, and |long
| = |ulong
| = 264.Integer
or Number
.
The result is the closest IEEE double-precision floating-point value using the IEEE round-to-nearest mode. 0 always becomes
+0
. Due to the possibility of an inexact result, awarning is generated if type M is long
or
ulong
unless this coercion is done as a cast.float
. The
result is the closest IEEE single-precision floating-point value using the IEEE round-to-nearest mode. 0 always becomes
+0
. Due to the possibility of an inexact result, awarning is generated if type M is int
, uint
,
long
, or ulong
unless this coercion is done as a cast.float
value m can be coerced to type Number
. The result is always exact.The following casts can be used:
float
or Number
value v can be cast to one of the machine integer types M.
First v is truncated to an integer i, truncating towards zero. Then, if i is not within
range of the target type M, it is treated modulo |M|. The result is i with the machine
type M. +0
, -0
, Infinity
, -Infinity
, and NaN
all cast to the machine integer 0.Number
value v can be cast to type float
. If inexact, the cast is done using
the IEEE round-to-nearest mode. +0
, -0
, Infinity
, -Infinity
, and NaN
all cast to their float
equivalents.Of course, any coercion can also be used as a cast.
When applied to a value with machine type M, the unary negation operator -
always returns a value
of the same type M. If the result is not within range of type M, it is treated modulo |M|.
Machine integers support the binary arithmetic operators +
, -
, *
, /
,
%
and bitwise logical operations ~
, &
, |
, ^
. If supplied
two operands of different machine integer types M1 and M2,
all of these binary operators 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 of +
, -
, *
, /
, %
is a machine integer
m of type M and the other is a Number
or float
value, then m is first
coerced to type Number
or float
. Next, if both operands are float
s, then the result
is a float
; otherwise the result is a Number
.
Machine integers also support bitwise shifts <<
, >>
, and >>>
.
The result has the same as the first operand. The second operand's type can be Number
or any machine type and
does not affect the type of the result. Right shifts using >>
are signed if the first operand has type
byte
, short
, int
, or long
, and unsigned if it has type ubyte
,
ushort
, uint
, or ulong
. Right shifts using >>>
are always unsigned.
If passed a float
argument, the bitwise logical operations ~
, &
, |
,
^
first coerce the float
to a Number
. If passed a float
as the first argument,
the bitwise shifts <<
, >>
, >>>
first coerce the float
to a Number
.
The comparison operators ==
, !=
, <
, >
, <=
, =>
allow any combination of machine type or Number
operands. They always compare the exact mathematical values without
first converting one operand's type to the other's. Comparisons involving NaNs are always false, and positive and negative
zeros compare equal.
The identity comparisons ===
and !==
treat all nine machine type values as disjoint from each
other and from regular Number
values. Thus, 7B !== 7
.
The unary operator !
v behaves the same as v!=0
when v has any
machine type.
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 2000000000I
is indistinguishable from 2000000000L
.
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,
2000000000L
+
2000000000L
should yield 4000000000L
. However, we assumed
that 2000000000L
is indistinguishable from 2000000000I
, so 2000000000I
+
2000000000I
should also yield 4000000000L
, 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+1I
, then we have to use 64-bit arithmetic
because the result could be 2147483648. However, if we compute var r:int = a+1I
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 if (a+1I > 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.
Waldemar Horwat Last modified Wednesday, February 16, 2000 |