Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 38 additions & 73 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -5316,39 +5316,54 @@ <h1>
</emu-note>
</emu-clause>

<emu-clause id="sec-toint32" type="abstract operation">
<emu-clause id="sec-tofixedsizeinteger" type="abstract operation">
<h1>
ToInt32 (
ToFixedSizeInteger (
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this now performs no type conversion, "IntegerToFixedSize" might be a better name. Opinions are welcome.

_argument_: an ECMAScript language value,
): either a normal completion containing an integral Number or a throw completion
_signed_: ~unsigned~ or ~signed~,
_bitWidth_: a positive integer,
_type_: ~number~ or ~bigint~,
Copy link
Copy Markdown
Member Author

@gibson042 gibson042 Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These three arguments are partially redundant with what TypedArray element type describes in a single enumeration, but the latter also includes FLOAT{16,32,64} and I couldn't find a clean way to layer separation of just the INT/BIGINT values so I've left that as a potential exercise for the future (but ideas for the present are nonetheless welcome).

I suspect that going that route would involve something like introducing ToFloat{16,32,64} operations, migrating the last four columns of that table into Abstract Operations along with Is{Unsigned,UnclampedInteger,BigInt}ElementType for use by ToFixedSizeInteger, and simplifying e.g. NumericToRawBytes/NumericToRawBytes/etc.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three separate abstract operations seem a bit nicer, because it more clearly separates conversion from and to Number resp. BigInt.

ToFixedSizeInteger (
  _argument_: an integer,
  _signed_: ~unsigned~ or ~signed~,
  _bitWidth_: a positive integer,
): an integer
  1. Let _fixedInt_ be _int_ modulo 2<sup>_bitWidth_</sup>.
  1. If _signed_ is ~signed~ and _fixedInt_ ≥ 2<sup>_bitWidth_ - 1</sup>, return _fixedInt_ - 2<sup>_bitWidth_</sup>.
  1. Return _fixedInt_.


ToFixedSizeNumber(
  _argument_: an ECMAScript language value,
  _signed_: ~unsigned~ or ~signed~,
  _bitWidth_: a positive integer,
): either a normal completion containing an integral Number or a throw completion
  1. Let _int_ be ? ToIntegerOrInfinity(_argument_).
  1. If _int_ is not finite, return 0.
  1. Return 𝔽(ToFixedSizeInteger(_int_, _signed_, _bitWidth_)).


ToFixedSizeBigInt(
  _argument_: an ECMAScript language value,
  _signed_: ~unsigned~ or ~signed~,
  _bitWidth_: a positive integer,
): either a normal completion containing a BigInt or a throw completion
  1. Let _int_ be ℝ(? ToBigInt(_argument_)).
  1. Return ℤ(ToFixedSizeInteger(_int_, _signed_, _bitWidth_)).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion. I took a half step in this direction, keeping the single new operation but making it infallibly map extended mathematical values and moving initial conversion to call sites.

): either a normal completion containing an integer or a throw completion
</h1>
<dl class="header">
<dt>description</dt>
<dd>It converts _argument_ to one of 2<sup>32</sup> integral Number values in the inclusive interval from 𝔽(-2<sup>31</sup>) to 𝔽(2<sup>31</sup> - 1).</dd>
<dd>It converts _argument_ to one of 2<sup>_bitWidth_</sup> integers in the inclusive interval from 0 to 2<sup>_bitWidth_</sup> - 1 (if _signed_ is ~unsigned~) or -2<sup>_bitWidth_ - 1</sup> to 2<sup>_bitWidth_ - 1</sup> - 1 (if _signed_ is ~signed~).</dd>
</dl>
<emu-alg>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
1. If _int_ is either +∞ or -∞, return 0.

(editorial convention)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear (despite GitHub bug), my suggestion is to insert "either".

Copy link
Copy Markdown
Member Author

@gibson042 gibson042 May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find the comparison conventions terribly confusing/incomplete and possibly contradictory, but since int is a mathematical value, shouldn't this actually be If _int_ = +∞ or _int_ = -∞, return 0. as seen at BinaryOr, BigIntBitwiseOp, and IsWordChar? I almost always see the "is either $A or $B" form with $A and $B being language values and spec enums.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, it's an 'extended mathematical value', which the comparison conventions don't mention, but lumping it in with mathematical values would be reasonable. So yeah, using = seems more consistent.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

1. Let _int_ be ? ToIntegerOrInfinity(_argument_).
1. If _int_ is not finite, return *+0*<sub>𝔽</sub>.
1. Let _int32bit_ be _int_ modulo 2<sup>32</sup>.
1. If _int32bit_ ≥ 2<sup>31</sup>, return 𝔽(_int32bit_ - 2<sup>32</sup>).
1. Return 𝔽(_int32bit_).
1. If _type_ is ~number~, let _int_ be ? ToIntegerOrInfinity(_argument_); else let _int_ be ℝ(? ToBigInt(_argument_)).
1. If _int_ is not finite, return 0.
1. Let _fixedInt_ be _int_ modulo 2<sup>_bitWidth_</sup>.
1. If _signed_ is ~signed~ and _fixedInt_ ≥ 2<sup>_bitWidth_ - 1</sup>, return _fixedInt_ - 2<sup>_bitWidth_</sup>.
1. Return _fixedInt_.
</emu-alg>
<emu-note>
<p>Given the above definition:</p>
<ul>
<li>
It is idempotent: for any ECMAScript language value _x_, ToInt32(ToInt32(_x_)) is the same as ToInt32(_x_).
</li>
<li>
For any ECMAScript language value _x_, ToInt32(ToUint32(_x_)) is the same as ToInt32(_x_). (It is to preserve this property that *+∞*<sub>𝔽</sub> and *-∞*<sub>𝔽</sub> are mapped to *+0*<sub>𝔽</sub>.)
It is idempotent: for any ECMAScript language value _x_, ToFixedSizeInteger(ToFixedSizeInteger(_x_, _signed_, _bitWidth_, _type_), _signed_, _bitWidth_, _type_) is the same as ToFixedSizeInteger(_x_, _signed_, _bitWidth_, _type_).
</li>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer us to retain something like the example from the old note. Something like ToFixedSizeInteger(ToFixedSizeInteger(_x_, ~signed~, 32), ~unsigned~, 32) is the same as ToFixedSizeInteger(_x_, ~unsigned~, 32).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

<li>
ToInt32 maps *-0*<sub>𝔽</sub> to *+0*<sub>𝔽</sub>.
It is bijective with respect to inversion of _signed_: for any ECMAScript language value _x_, ToFixedSizeInteger(ToFixedSizeInteger(_x_, ~unsigned~, _bitWidth_, _type_), ~signed~, _bitWidth_, _type_) is the same as ToFixedSizeInteger(_x_, ~signed~, _bitWidth_, _type_) and ToFixedSizeInteger(ToFixedSizeInteger(_x_, ~signed~, _bitWidth_, _type_), ~unsigned~, _bitWidth_, _type_) is the same as ToFixedSizeInteger(_x_, ~unsigned~, _bitWidth_, _type_). (It is to preserve this property that +∞ and -∞ are mapped to 0.)
</li>
</ul>
</emu-note>
</emu-clause>

<emu-clause id="sec-toint32" type="abstract operation">
<h1>
ToInt32 (
_argument_: an ECMAScript language value,
): either a normal completion containing an integral Number or a throw completion
</h1>
<dl class="header">
<dt>description</dt>
<dd>It converts _argument_ to one of 2<sup>32</sup> integral Number values in the inclusive interval from 𝔽(-2<sup>31</sup>) to 𝔽(2<sup>31</sup> - 1), excluding *-0*<sub>𝔽</sub>.</dd>
</dl>
<emu-alg>
1. Return 𝔽(? ToFixedSizeInteger(_argument_, ~signed~, 32, ~number~)).
</emu-alg>
</emu-clause>

<emu-clause id="sec-touint32" type="abstract operation">
<h1>
ToUint32 (
Expand All @@ -5360,28 +5375,8 @@ <h1>
<dd>It converts _argument_ to one of 2<sup>32</sup> integral Number values in the inclusive interval from *+0*<sub>𝔽</sub> to 𝔽(2<sup>32</sup> - 1).</dd>
</dl>
<emu-alg>
1. Let _int_ be ? ToIntegerOrInfinity(_argument_).
1. If _int_ is not finite, return *+0*<sub>𝔽</sub>.
1. Let _int32bit_ be _int_ modulo 2<sup>32</sup>.
1. [id="step-touint32-return"] Return 𝔽(_int32bit_).
1. Return 𝔽(? ToFixedSizeInteger(_argument_, ~unsigned~, 32, ~number~)).
</emu-alg>
<emu-note>
<p>Given the above definition:</p>
<ul>
<li>
Step <emu-xref href="#step-touint32-return"></emu-xref> is the only difference between ToUint32 and ToInt32.
</li>
<li>
It is idempotent: for any ECMAScript language value _x_, ToUint32(ToUint32(_x_)) is the same as ToUint32(_x_).
</li>
<li>
For any ECMAScript language value _x_, ToUint32(ToInt32(_x_)) is the same as ToUint32(_x_). (It is to preserve this property that *+∞*<sub>𝔽</sub> and *-∞*<sub>𝔽</sub> are mapped to *+0*<sub>𝔽</sub>.)
</li>
<li>
ToUint32 maps *-0*<sub>𝔽</sub> to *+0*<sub>𝔽</sub>.
</li>
</ul>
</emu-note>
</emu-clause>

<emu-clause id="sec-toint16" type="abstract operation">
Expand All @@ -5392,14 +5387,10 @@ <h1>
</h1>
<dl class="header">
<dt>description</dt>
<dd>It converts _argument_ to one of 2<sup>16</sup> integral Number values in the inclusive interval from 𝔽(-2<sup>15</sup>) to 𝔽(2<sup>15</sup> - 1).</dd>
<dd>It converts _argument_ to one of 2<sup>16</sup> integral Number values in the inclusive interval from 𝔽(-2<sup>15</sup>) to 𝔽(2<sup>15</sup> - 1), excluding *-0*<sub>𝔽</sub>.</dd>
</dl>
<emu-alg>
1. Let _int_ be ? ToIntegerOrInfinity(_argument_).
1. If _int_ is not finite, return *+0*<sub>𝔽</sub>.
1. Let _int16bit_ be _int_ modulo 2<sup>16</sup>.
1. If _int16bit_ ≥ 2<sup>15</sup>, return 𝔽(_int16bit_ - 2<sup>16</sup>).
1. Return 𝔽(_int16bit_).
1. Return 𝔽(? ToFixedSizeInteger(_argument_, ~signed~, 16, ~number~)).
</emu-alg>
</emu-clause>

Expand All @@ -5414,22 +5405,8 @@ <h1>
<dd>It converts _argument_ to one of 2<sup>16</sup> integral Number values in the inclusive interval from *+0*<sub>𝔽</sub> to 𝔽(2<sup>16</sup> - 1).</dd>
</dl>
<emu-alg>
1. Let _int_ be ? ToIntegerOrInfinity(_argument_).
1. If _int_ is not finite, return *+0*<sub>𝔽</sub>.
1. [id="step-touint16-mod"] Let _int16bit_ be _int_ modulo 2<sup>16</sup>.
1. Return 𝔽(_int16bit_).
1. Return 𝔽(? ToFixedSizeInteger(_argument_, ~unsigned~, 16, ~number~)).
</emu-alg>
<emu-note>
<p>Given the above definition:</p>
<ul>
<li>
The substitution of 2<sup>16</sup> for 2<sup>32</sup> in step <emu-xref href="#step-touint16-mod"></emu-xref> is the only difference between ToUint32 and ToUint16.
</li>
<li>
ToUint16 maps *-0*<sub>𝔽</sub> to *+0*<sub>𝔽</sub>.
</li>
</ul>
</emu-note>
</emu-clause>

<emu-clause id="sec-toint8" type="abstract operation">
Expand All @@ -5440,14 +5417,10 @@ <h1>
</h1>
<dl class="header">
<dt>description</dt>
<dd>It converts _argument_ to one of 2<sup>8</sup> integral Number values in the inclusive interval from *-128*<sub>𝔽</sub> to *127*<sub>𝔽</sub>.</dd>
<dd>It converts _argument_ to one of 2<sup>8</sup> integral Number values in the inclusive interval from *-128*<sub>𝔽</sub> to *127*<sub>𝔽</sub>, excluding *-0*<sub>𝔽</sub>.</dd>
</dl>
<emu-alg>
1. Let _int_ be ? ToIntegerOrInfinity(_argument_).
1. If _int_ is not finite, return *+0*<sub>𝔽</sub>.
1. Let _int8bit_ be _int_ modulo 2<sup>8</sup>.
1. If _int8bit_ ≥ 2<sup>7</sup>, return 𝔽(_int8bit_ - 2<sup>8</sup>).
1. Return 𝔽(_int8bit_).
1. Return 𝔽(? ToFixedSizeInteger(_argument_, ~signed~, 8, ~number~)).
</emu-alg>
</emu-clause>

Expand All @@ -5462,10 +5435,7 @@ <h1>
<dd>It converts _argument_ to one of 2<sup>8</sup> integral Number values in the inclusive interval from *+0*<sub>𝔽</sub> to *255*<sub>𝔽</sub>.</dd>
</dl>
<emu-alg>
1. Let _int_ be ? ToIntegerOrInfinity(_argument_).
1. If _int_ is not finite, return *+0*<sub>𝔽</sub>.
1. Let _int8bit_ be _int_ modulo 2<sup>8</sup>.
1. Return 𝔽(_int8bit_).
1. Return 𝔽(? ToFixedSizeInteger(_argument_, ~unsigned~, 8, ~number~)).
</emu-alg>
</emu-clause>

Expand Down Expand Up @@ -5640,10 +5610,7 @@ <h1>
<dd>It converts _argument_ to one of 2<sup>64</sup> BigInt values in the inclusive interval from ℤ(-2<sup>63</sup>) to ℤ(2<sup>63</sup> - 1).</dd>
</dl>
<emu-alg>
1. Let _n_ be ? ToBigInt(_argument_).
1. Let _int64bit_ be ℝ(_n_) modulo 2<sup>64</sup>.
1. If _int64bit_ ≥ 2<sup>63</sup>, return ℤ(_int64bit_ - 2<sup>64</sup>).
1. Return ℤ(_int64bit_).
1. Return ℤ(? ToFixedSizeInteger(_argument_, ~signed~, 64, ~bigint~)).
</emu-alg>
</emu-clause>

Expand All @@ -5658,9 +5625,7 @@ <h1>
<dd>It converts _argument_ to one of 2<sup>64</sup> BigInt values in the inclusive interval from *0*<sub>ℤ</sub> to ℤ(2<sup>64</sup> - 1).</dd>
</dl>
<emu-alg>
1. Let _n_ be ? ToBigInt(_argument_).
1. Let _int64bit_ be ℝ(_n_) modulo 2<sup>64</sup>.
1. Return ℤ(_int64bit_).
1. Return ℤ(? ToFixedSizeInteger(_argument_, ~unsigned~, 64, ~bigint~)).
</emu-alg>
</emu-clause>

Expand Down
Loading