Skip to content

Commit

Permalink
Editorial: Use the rounding abstract operations from Intl.NumberForma…
Browse files Browse the repository at this point in the history
…t V3

We intend to standardize on the same rounding modes as Intl.NumberFormat
(see #1038), so it makes sense to use the same abstract operations, namely
GetUnsignedRoundingMode and ApplyUnsignedRoundingMode.

Note that this does not yet add user-visible support for the full set of
rounding modes that Intl.NumberFormat does; that (#1038) is a normative
change that we'll apply once Intl.NumberFormat goes to Stage 4.

However, this way of expressing rounding makes it easier to make that
change in the future, and makes it easier to address #2191 in the short
term.

RoundTowardsZero stays the same, since that is used for several other
things as well as rounding according to a rounding mode.
  • Loading branch information
ptomato authored and Ms2ger committed May 19, 2022
1 parent 7a4a518 commit 75279e5
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 37 deletions.
196 changes: 173 additions & 23 deletions spec/abstractops.html
Original file line number Diff line number Diff line change
Expand Up @@ -651,40 +651,190 @@ <h1>
</emu-alg>
</emu-clause>

<emu-clause id="sec-temporal-roundhalfawayfromzero" aoid="RoundHalfAwayFromZero">
<h1>RoundHalfAwayFromZero ( _x_ )</h1>
<emu-clause id="sec-temporal-getunsignedroundingmode" type="abstract operation">
<h1>
GetUnsignedRoundingMode (
_roundingMode_: *"ceil"*, *"floor"*, *"expand"*, *"trunc"*, *"halfCeil"*, *"halfFloor"*, *"halfExpand"*, *"halfTrunc"*, or *"halfEven"*,
_isNegative_: *true* or *false*,
): ~zero~, ~infinity~, ~half-zero~, ~half-infinity~, or ~half-even~
</h1>
<dl class="header">
<dt>description</dt>
<dd>
The return value is the rounding mode that should be applied to the absolute value of a number to produce the same result as if _roundingMode_ were applied to the signed value of the number (negative if _isNegative_ is *true*, or positive otherwise).
</dd>
</dl>
<emu-alg>
1. Assert: _x_ is a mathematical value.
1. Return the mathematical value that is closest to _x_ and is an integer.
If two integers are equally close to _x_, then the result is the integer that is farther away from 0. If _x_ is already an integer, then the result is _x_.
1. If _isNegative_ is *true*, return the specification type in the third column of <emu-xref href="#table-temporal-unsigned-rounding-modes"></emu-xref> where the first column is _roundingMode_ and the second column is "negative".
1. Else, return the specification type in the third column of <emu-xref href="#table-temporal-unsigned-rounding-modes"></emu-xref> where the first column is _roundingMode_ and the second column is "positive".
</emu-alg>
<emu-note type="editor">
<p>This operation is intended to be the same one as in the <a href="https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/diff.html#sec-getunsignedroundingmode">Intl.NumberFormat v3</a> proposal.</p>
</emu-note>
<emu-table id="table-temporal-unsigned-rounding-modes">
<emu-caption>Conversion from rounding mode to unsigned rounding mode</emu-caption>
<table class="real-table">
<thead>
<tr>
<th>Identifier</th>
<th>Sign</th>
<th>Unsigned Rounding Mode</th>
</tr>
</thead>
<tr>
<td rowspan="2">*"ceil"*</td>
<td>positive</td>
<td>~infinity~</td>
</tr>
<tr>
<td>negative</td>
<td>~zero~</td>
</tr>
<tr>
<td rowspan="2">*"floor"*</td>
<td>positive</td>
<td>~zero~</td>
</tr>
<tr>
<td>negative</td>
<td>~infinity~</td>
</tr>
<tr>
<td rowspan="2">*"expand"*</td>
<td>positive</td>
<td>~infinity~</td>
</tr>
<tr>
<td>negative</td>
<td>~infinity~</td>
</tr>
<tr>
<td rowspan="2">*"trunc"*</td>
<td>positive</td>
<td>~zero~</td>
</tr>
<tr>
<td>negative</td>
<td>~zero~</td>
</tr>
<tr>
<td rowspan="2">*"halfCeil"*</td>
<td>positive</td>
<td>~half-infinity~</td>
</tr>
<tr>
<td>negative</td>
<td>~half-zero~</td>
</tr>
<tr>
<td rowspan="2">*"halfFloor"*</td>
<td>positive</td>
<td>~half-zero~</td>
</tr>
<tr>
<td>negative</td>
<td>~half-infinity~</td>
</tr>
<tr>
<td rowspan="2">*"halfExpand"*</td>
<td>positive</td>
<td>~half-infinity~</td>
</tr>
<tr>
<td>negative</td>
<td>~half-infinity~</td>
</tr>
<tr>
<td rowspan="2">*"halfTrunc"*</td>
<td>positive</td>
<td>~half-zero~</td>
</tr>
<tr>
<td>negative</td>
<td>~half-zero~</td>
</tr>
<tr>
<td rowspan="2">*"halfEven"*</td>
<td>positive</td>
<td>~half-even~</td>
</tr>
<tr>
<td>negative</td>
<td>~half-even~</td>
</tr>
</table>
</emu-table>
</emu-clause>

<emu-clause id="sec-temporal-roundnumbertoincrement" aoid="RoundNumberToIncrement">
<h1>RoundNumberToIncrement ( _x_, _increment_, _roundingMode_ )</h1>
<p>
The abstract operation RoundNumberToIncrement rounds the mathematical value _x_ to the nearest multiple of the integer _increment_.
It rounds up or down according to _roundingMode_.
</p>
<emu-clause id="sec-temporal-applyunsignedroundingmode" type="abstract operation">
<h1>
ApplyUnsignedRoundingMode (
_x_: a mathematical value,
_r1_: a mathematical value,
_r2_: a mathematical value,
_unsignedRoundingMode_: ~zero~, ~infinity~, ~half-zero~, ~half-infinity~, ~half-even~, or *undefined*,
): a mathematical value
</h1>
<dl class="header">
<dt>description</dt>
<dd>
It considers _x_, bracketed below by _r1_ and above by _r2_, and returns either _r1_ or _r2_ according to _unsignedRoundingMode_.
</dd>
</dl>
<emu-alg>
1. If _x_ is equal to _r1_, return _r1_.
1. Assert: _r1_ &lt; _x_ &lt; _r2_.
1. Assert: _unsignedRoundingMode_ is not *undefined*.
1. If _unsignedRoundingMode_ is ~zero~, return _r1_.
1. If _unsignedRoundingMode_ is ~infinity~, return _r2_.
1. Let _d1_ be <emu-eqn>_x__r1_</emu-eqn>.
1. Let _d2_ be <emu-eqn>_r2__x_</emu-eqn>.
1. If _d1_ &lt; _d2_, return _r1_.
1. If _d2_ &lt; _d1_, return _r2_.
1. Assert: _d1_ is equal to _d2_.
1. If _unsignedRoundingMode_ is ~half-zero~, return _r1_.
1. If _unsignedRoundingMode_ is ~half-infinity~, return _r2_.
1. Assert: _unsignedRoundingMode_ is ~half-even~.
1. Let _cardinality_ be <emu-eqn>(_r1_ / (_r2__r1_)) modulo 2</emu-eqn>.
1. If _cardinality_ is 0, return _r1_.
1. Return _r2_.
</emu-alg>
<emu-note type="editor">
<p>
The rounding modes accepted by this abstract operation are intended to be the same as whatever is eventually standardized in the <a href="https://github.com/tc39/proposal-intl-numberformat-v3">Intl.NumberFormat V3</a> proposal.
</p>
<p>This operation is intended to be the same one as in the <a href="https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/diff.html#sec-applyunsignedroundingmode">Intl.NumberFormat v3</a> proposal.</p>
</emu-note>
</emu-clause>

<emu-clause id="sec-temporal-roundnumbertoincrement" type="abstract operation">
<h1>
RoundNumberToIncrement (
_x_: a mathematical value,
_increment_: an integer,
_roundingMode_: *"ceil"*, *"floor"*, *"trunc"*, or *"halfExpand"*,
): an integer
</h1>
<dl class="header">
<dt>description</dt>
<dd>It rounds _x_ to the nearest multiple of _increment_, up or down according to _roundingMode_.</dd>
</dl>
<emu-alg>
1. Assert: _x_ and _increment_ are mathematical values.
1. Assert: _roundingMode_ is *"ceil"*, *"floor"*, *"trunc"*, or *"halfExpand"*.
1. Let _quotient_ be _x_ / _increment_.
1. If _roundingMode_ is *"ceil"*, then
1. Let _rounded_ be -floor(-_quotient_).
1. Else if _roundingMode_ is *"floor"*, then
1. Let _rounded_ be floor(_quotient_).
1. Else if _roundingMode_ is *"trunc"*, then
1. Let _rounded_ be RoundTowardsZero(_quotient_).
1. If _quotient_ &lt; 0, then
1. Let _isNegative_ be *true*.
1. Set _quotient_ to -_quotient_.
1. Else,
1. Let _rounded_ be ! RoundHalfAwayFromZero(_quotient_).
1. Let _isNegative_ be *false*.
1. Let _unsignedRoundingMode_ be GetUnsignedRoundingMode(_roundingMode_, _isNegative_).
1. Let _r1_ be the largest integer such that _r1__quotient_.
1. Let _r2_ be the smallest integer such that _r2_ &gt; _quotient_.
1. Let _rounded_ be ApplyUnsignedRoundingMode(_quotient_, _r1_, _r2_, _unsignedRoundingMode_).
1. If _isNegative_ is *true*, set _rounded_ to -_rounded_.
1. Return _rounded_ &times; _increment_.
</emu-alg>
<emu-note type="editor">
<p>
The rounding modes accepted by this abstract operation are intended to be the same as whatever is eventually standardized in the <a href="https://github.com/tc39/proposal-intl-numberformat-v3">Intl.NumberFormat V3</a> proposal.
</p>
</emu-note>
</emu-clause>

<emu-clause id="sec-temporal-iso8601grammar">
Expand Down
20 changes: 10 additions & 10 deletions spec/duration.html
Original file line number Diff line number Diff line change
Expand Up @@ -1621,7 +1621,7 @@ <h1>
1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _relativeTo_, _oneYear_).
1. Let _oneYearDays_ be _moveResult_.[[Days]].
1. Let _fractionalYears_ be _years_ + _days_ / abs(_oneYearDays_).
1. Set _years_ to ! RoundNumberToIncrement(_fractionalYears_, _increment_, _roundingMode_).
1. Set _years_ to RoundNumberToIncrement(_fractionalYears_, _increment_, _roundingMode_).
1. Set _remainder_ to _fractionalYears_ - _years_.
1. Set _months_, _weeks_, and _days_ to 0.
1. Else if _unit_ is *"month"*, then
Expand All @@ -1645,7 +1645,7 @@ <h1>
1. Set _relativeTo_ to _moveResult_.[[RelativeTo]].
1. Set _oneMonthDays_ to _moveResult_.[[Days]].
1. Let _fractionalMonths_ be _months_ + _days_ / abs(_oneMonthDays_).
1. Set _months_ to ! RoundNumberToIncrement(_fractionalMonths_, _increment_, _roundingMode_).
1. Set _months_ to RoundNumberToIncrement(_fractionalMonths_, _increment_, _roundingMode_).
1. Set _remainder_ to _fractionalMonths_ - _months_.
1. Set _weeks_ and _days_ to 0.
1. Else if _unit_ is *"week"*, then
Expand All @@ -1661,41 +1661,41 @@ <h1>
1. Set _relativeTo_ to _moveResult_.[[RelativeTo]].
1. Set _oneWeekDays_ to _moveResult_.[[Days]].
1. Let _fractionalWeeks_ be _weeks_ + _days_ / abs(_oneWeekDays_).
1. Set _weeks_ to ! RoundNumberToIncrement(_fractionalWeeks_, _increment_, _roundingMode_).
1. Set _weeks_ to RoundNumberToIncrement(_fractionalWeeks_, _increment_, _roundingMode_).
1. Set _remainder_ to _fractionalWeeks_ - _weeks_.
1. Set _days_ to 0.
1. Else if _unit_ is *"day"*, then
1. Let _fractionalDays_ be _days_.
1. Set _days_ to ! RoundNumberToIncrement(_days_, _increment_, _roundingMode_).
1. Set _days_ to RoundNumberToIncrement(_days_, _increment_, _roundingMode_).
1. Set _remainder_ to _fractionalDays_ - _days_.
1. Else if _unit_ is *"hour"*, then
1. Let _fractionalHours_ be (_fractionalSeconds_ / 60 + _minutes_) / 60 + _hours_.
1. Set _hours_ to ! RoundNumberToIncrement(_fractionalHours_, _increment_, _roundingMode_).
1. Set _hours_ to RoundNumberToIncrement(_fractionalHours_, _increment_, _roundingMode_).
1. Set _remainder_ to _fractionalHours_ - _hours_.
1. Set _minutes_, _seconds_, _milliseconds_, _microseconds_, and _nanoseconds_ to 0.
1. Else if _unit_ is *"minute"*, then
1. Let _fractionalMinutes_ be _fractionalSeconds_ / 60 + _minutes_.
1. Set _minutes_ to ! RoundNumberToIncrement(_fractionalMinutes_, _increment_, _roundingMode_).
1. Set _minutes_ to RoundNumberToIncrement(_fractionalMinutes_, _increment_, _roundingMode_).
1. Set _remainder_ to _fractionalMinutes_ - _minutes_.
1. Set _seconds_, _milliseconds_, _microseconds_, and _nanoseconds_ to 0.
1. Else if _unit_ is *"second"*, then
1. Set _seconds_ to ! RoundNumberToIncrement(_fractionalSeconds_, _increment_, _roundingMode_).
1. Set _seconds_ to RoundNumberToIncrement(_fractionalSeconds_, _increment_, _roundingMode_).
1. Set _remainder_ to _fractionalSeconds_ - _seconds_.
1. Set _milliseconds_, _microseconds_, and _nanoseconds_ to 0.
1. Else if _unit_ is *"millisecond"*, then
1. Let _fractionalMilliseconds_ be _nanoseconds_ &times; 10<sup>-6</sup> + _microseconds_ &times; 10<sup>-3</sup> + _milliseconds_.
1. Set _milliseconds_ to ! RoundNumberToIncrement(_fractionalMilliseconds_, _increment_, _roundingMode_).
1. Set _milliseconds_ to RoundNumberToIncrement(_fractionalMilliseconds_, _increment_, _roundingMode_).
1. Set _remainder_ to _fractionalMilliseconds_ - _milliseconds_.
1. Set _microseconds_ and _nanoseconds_ to 0.
1. Else if _unit_ is *"microsecond"*, then
1. Let _fractionalMicroseconds_ be _nanoseconds_ &times; 10<sup>-3</sup> + _microseconds_.
1. Set _microseconds_ to ! RoundNumberToIncrement(_fractionalMicroseconds_, _increment_, _roundingMode_).
1. Set _microseconds_ to RoundNumberToIncrement(_fractionalMicroseconds_, _increment_, _roundingMode_).
1. Set _remainder_ to _fractionalMicroseconds_ - _microseconds_.
1. Set _nanoseconds_ to 0.
1. Else,
1. Assert: _unit_ is *"nanosecond"*.
1. Set _remainder_ to _nanoseconds_.
1. Set _nanoseconds_ to ! RoundNumberToIncrement(_nanoseconds_, _increment_, _roundingMode_).
1. Set _nanoseconds_ to RoundNumberToIncrement(_nanoseconds_, _increment_, _roundingMode_).
1. Set _remainder_ to _remainder_ - _nanoseconds_.
1. Let _duration_ be ! CreateDurationRecord(_years_, _months_, _weeks_, _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
1. Return the Record {
Expand Down
2 changes: 1 addition & 1 deletion spec/instant.html
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@ <h1>RoundTemporalInstant ( _ns_, _increment_, _unit_, _roundingMode_ )</h1>
1. Else,
1. Assert: _unit_ is *"nanosecond"*.
1. Let _incrementNs_ be _increment_.
1. Return ! RoundNumberToIncrement(ℝ(_ns_), _incrementNs_, _roundingMode_).
1. Return RoundNumberToIncrement(ℝ(_ns_), _incrementNs_, _roundingMode_).
</emu-alg>
</emu-clause>

Expand Down
2 changes: 1 addition & 1 deletion spec/plaintime.html
Original file line number Diff line number Diff line change
Expand Up @@ -909,7 +909,7 @@ <h1>RoundTime ( _hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanos
1. Else,
1. Assert: _unit_ is *"nanosecond"*.
1. Let _quantity_ be _nanosecond_.
1. Let _result_ be ! RoundNumberToIncrement(_quantity_, _increment_, _roundingMode_).
1. Let _result_ be RoundNumberToIncrement(_quantity_, _increment_, _roundingMode_).
1. If _unit_ is *"day"*, then
1. Return the Record {
[[Days]]: _result_,
Expand Down
2 changes: 1 addition & 1 deletion spec/timezone.html
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ <h1>FormatISOTimeZoneOffsetString ( _offsetNanoseconds_ )</h1>
</p>
<emu-alg>
1. Assert: _offsetNanoseconds_ is an integer.
1. Set _offsetNanoseconds_ to ! RoundNumberToIncrement(_offsetNanoseconds_, 60 &times; 10<sup>9</sup>, *"halfExpand"*).
1. Set _offsetNanoseconds_ to RoundNumberToIncrement(_offsetNanoseconds_, 60 &times; 10<sup>9</sup>, *"halfExpand"*).
1. If _offsetNanoseconds_ &ge; 0, let _sign_ be *"+"*; otherwise, let _sign_ be *"-"*.
1. Set _offsetNanoseconds_ to abs(_offsetNanoseconds_).
1. Let _minutes_ be _offsetNanoseconds_ / (60 &times; 10<sup>9</sup>) modulo 60.
Expand Down
2 changes: 1 addition & 1 deletion spec/zoneddatetime.html
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ <h1>
1. If _candidateNanoseconds_ = _offsetNanoseconds_, then
1. Return _candidate_.[[Nanoseconds]].
1. If _matchBehaviour_ is ~match minutes~, then
1. Let _roundedCandidateNanoseconds_ be ! RoundNumberToIncrement(_candidateNanoseconds_, 60 &times; 10<sup>9</sup>, *"halfExpand"*).
1. Let _roundedCandidateNanoseconds_ be RoundNumberToIncrement(_candidateNanoseconds_, 60 &times; 10<sup>9</sup>, *"halfExpand"*).
1. If _roundedCandidateNanoseconds_ = _offsetNanoseconds_, then
1. Return _candidate_.[[Nanoseconds]].
1. If _offsetOption_ is *"reject"*, throw a *RangeError* exception.
Expand Down

0 comments on commit 75279e5

Please sign in to comment.