Bug 1874684 - Part 14: Return DateDuration from DifferenceDate. r=sfink
[gecko.git] / js / src / builtin / temporal / TemporalRoundingMode.h
blob23d3996d0927c3aa71db1b2e0dfa7c9f2822baac
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef builtin_temporal_TemporalRoundingMode_h
8 #define builtin_temporal_TemporalRoundingMode_h
10 #include "mozilla/Assertions.h"
12 #include <cmath>
13 #include <stdint.h>
15 #include "builtin/temporal/Int128.h"
17 namespace js::temporal {
19 // Overview of integer rounding modes is available at
20 // <https://en.wikipedia.org/wiki/Rounding#Rounding_to_integer>.
21 enum class TemporalRoundingMode {
22 // 1. Directed rounding to an integer.
24 // Round toward positive infinity.
25 Ceil,
27 // Round toward negative infinity.
28 Floor,
30 // Round toward infinity or round away from zero.
31 Expand,
33 // Round toward zero or round away from infinity.
34 Trunc,
36 // 2. Rounding to the nearest integer.
38 // Round half toward positive infinity.
39 HalfCeil,
41 // Round half toward negative infinity.
42 HalfFloor,
44 // Round half toward infinity or round half away from zero.
45 HalfExpand,
47 // Round half toward zero or round half away from infinity.
48 HalfTrunc,
50 // Round half to even.
51 HalfEven,
54 /**
55 * NegateTemporalRoundingMode ( roundingMode )
57 constexpr auto NegateTemporalRoundingMode(TemporalRoundingMode roundingMode) {
58 // Steps 1-5.
59 switch (roundingMode) {
60 case TemporalRoundingMode::Ceil:
61 return TemporalRoundingMode::Floor;
63 case TemporalRoundingMode::Floor:
64 return TemporalRoundingMode::Ceil;
66 case TemporalRoundingMode::HalfCeil:
67 return TemporalRoundingMode::HalfFloor;
69 case TemporalRoundingMode::HalfFloor:
70 return TemporalRoundingMode::HalfCeil;
72 case TemporalRoundingMode::Expand:
73 case TemporalRoundingMode::Trunc:
74 case TemporalRoundingMode::HalfExpand:
75 case TemporalRoundingMode::HalfTrunc:
76 case TemporalRoundingMode::HalfEven:
77 return roundingMode;
79 MOZ_CRASH("invalid rounding mode");
82 /**
83 * Adjust the rounding mode to round negative values in the same direction as
84 * positive values.
86 constexpr auto ToPositiveRoundingMode(TemporalRoundingMode roundingMode) {
87 switch (roundingMode) {
88 case TemporalRoundingMode::Ceil:
89 case TemporalRoundingMode::Floor:
90 case TemporalRoundingMode::HalfCeil:
91 case TemporalRoundingMode::HalfFloor:
92 case TemporalRoundingMode::HalfEven:
93 // (Half-)Ceil/Floor round toward the same infinity for negative and
94 // positive values, so the rounding mode doesn't need to be adjusted. The
95 // same applies for half-even rounding.
96 return roundingMode;
98 case TemporalRoundingMode::Expand:
99 // Expand rounds positive values toward +infinity, but negative values
100 // toward -infinity. Adjust the rounding mode to Ceil to round negative
101 // values in the same direction as positive values.
102 return TemporalRoundingMode::Ceil;
104 case TemporalRoundingMode::Trunc:
105 // Truncation rounds positive values down toward zero, but negative values
106 // up toward zero. Adjust the rounding mode to Floor to round negative
107 // values in the same direction as positive values.
108 return TemporalRoundingMode::Floor;
110 case TemporalRoundingMode::HalfExpand:
111 // Adjust the rounding mode to Half-Ceil, similar to the Expand case.
112 return TemporalRoundingMode::HalfCeil;
114 case TemporalRoundingMode::HalfTrunc:
115 // Adjust the rounding mode to Half-Floor, similar to the Trunc case.
116 return TemporalRoundingMode::HalfFloor;
118 MOZ_CRASH("unexpected rounding mode");
121 // Temporal performs division on "mathematical values" [1] with implies using
122 // infinite precision. This rules out using IEE-754 floating point types like
123 // `double`. It also means we can't implement the algorithms from the
124 // specification verbatim, but instead have to translate them into equivalent
125 // operations.
127 // Throughout the following division functions, the divisor is required to be
128 // positive. This allows to simplify the implementation, because it ensures
129 // non-zero quotient and remainder values have the same sign as the dividend.
131 // [1] https://tc39.es/ecma262/#mathematical-value
134 * Compute ceiling division ⌈dividend / divisor⌉. The divisor must be a positive
135 * number.
137 constexpr int64_t CeilDiv(int64_t dividend, int64_t divisor) {
138 MOZ_ASSERT(divisor > 0, "negative divisor not supported");
140 // NB: Division and modulo operation are fused into a single machine code
141 // instruction by the compiler.
142 int64_t quotient = dividend / divisor;
143 int64_t remainder = dividend % divisor;
145 // Ceiling division rounds the quotient toward positive infinity. When the
146 // quotient is negative, this is equivalent to rounding toward zero. See [1].
148 // int64_t division truncates, so rounding toward zero for negative quotients
149 // is already covered. When there is a non-zero positive remainder, the
150 // quotient is positive and we have to increment it by one to implement
151 // rounding toward positive infinity.
153 // [1]
154 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
155 if (remainder > 0) {
156 quotient += 1;
158 return quotient;
162 * Compute floor division ⌊dividend / divisor⌋. The divisor must be a positive
163 * number.
165 constexpr int64_t FloorDiv(int64_t dividend, int64_t divisor) {
166 MOZ_ASSERT(divisor > 0, "negative divisor not supported");
168 // NB: Division and modulo operation are fused into a single machine code
169 // instruction by the compiler.
170 int64_t quotient = dividend / divisor;
171 int64_t remainder = dividend % divisor;
173 // Floor division rounds the quotient toward negative infinity. When the
174 // quotient is positive, this is equivalent to rounding toward zero. See [1].
176 // int64_t division truncates, so rounding toward zero for positive quotients
177 // is already covered. When there is a non-zero negative remainder, the
178 // quotient is negative and we have to decrement it by one to implement
179 // rounding toward negative infinity.
181 // [1]
182 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
183 if (remainder < 0) {
184 quotient -= 1;
186 return quotient;
190 * Compute "round toward infinity" division `dividend / divisor`. The divisor
191 * must be a positive number.
193 constexpr int64_t ExpandDiv(int64_t dividend, int64_t divisor) {
194 MOZ_ASSERT(divisor > 0, "negative divisor not supported");
196 // NB: Division and modulo operation are fused into a single machine code
197 // instruction by the compiler.
198 int64_t quotient = dividend / divisor;
199 int64_t remainder = dividend % divisor;
201 // "Round toward infinity" division rounds positive quotients toward positive
202 // infinity and negative quotients toward negative infinity. See [1].
204 // When there is a non-zero positive remainder, the quotient is positive and
205 // we have to increment it by one to implement rounding toward positive
206 // infinity. When there is a non-zero negative remainder, the quotient is
207 // negative and we have to decrement it by one to implement rounding toward
208 // negative infinity.
210 // [1]
211 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
212 if (remainder > 0) {
213 quotient += 1;
215 if (remainder < 0) {
216 quotient -= 1;
218 return quotient;
222 * Compute truncating division `dividend / divisor`. The divisor must be a
223 * positive number.
225 constexpr int64_t TruncDiv(int64_t dividend, int64_t divisor) {
226 MOZ_ASSERT(divisor > 0, "negative divisor not supported");
228 // Truncating division rounds both positive and negative quotients toward
229 // zero, cf. [1].
231 // int64_t division truncates, so rounding toward zero implicitly happens.
233 // [1]
234 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
235 return dividend / divisor;
239 * Compute "round half toward positive infinity" division `dividend / divisor`.
240 * The divisor must be a positive number.
242 inline int64_t HalfCeilDiv(int64_t dividend, int64_t divisor) {
243 MOZ_ASSERT(divisor > 0, "negative divisor not supported");
245 // NB: Division and modulo operation are fused into a single machine code
246 // instruction by the compiler.
247 int64_t quotient = dividend / divisor;
248 int64_t remainder = dividend % divisor;
250 // "Round half toward positive infinity" division rounds the quotient toward
251 // positive infinity when the fractional part of the remainder is ≥0.5. When
252 // the quotient is negative, this is equivalent to rounding toward zero
253 // instead of toward positive infinity. See [1].
255 // When the remainder is a non-zero positive value, the quotient is positive,
256 // too. When additionally the fractional part of the remainder is ≥0.5, we
257 // have to increment the quotient by one to implement rounding toward positive
258 // infinity.
260 // int64_t division truncates, so we implicitly round toward zero for negative
261 // quotients. When the absolute value of the fractional part of the remainder
262 // is >0.5, we should instead have rounded toward negative infinity, so we
263 // need to decrement the quotient by one.
265 // [1]
266 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
267 if (remainder > 0 && uint64_t(std::abs(remainder)) * 2 >= uint64_t(divisor)) {
268 quotient += 1;
270 if (remainder < 0 && uint64_t(std::abs(remainder)) * 2 > uint64_t(divisor)) {
271 quotient -= 1;
273 return quotient;
277 * Compute "round half toward negative infinity" division `dividend / divisor`.
278 * The divisor must be a positive number.
280 inline int64_t HalfFloorDiv(int64_t dividend, int64_t divisor) {
281 MOZ_ASSERT(divisor > 0, "negative divisor not supported");
283 // NB: Division and modulo operation are fused into a single machine code
284 // instruction by the compiler.
285 int64_t quotient = dividend / divisor;
286 int64_t remainder = dividend % divisor;
288 // "Round half toward negative infinity" division rounds the quotient toward
289 // negative infinity when the fractional part of the remainder is ≥0.5. When
290 // the quotient is positive, this is equivalent to rounding toward zero
291 // instead of toward negative infinity. See [1].
293 // When the remainder is a non-zero negative value, the quotient is negative,
294 // too. When additionally the fractional part of the remainder is ≥0.5, we
295 // have to decrement the quotient by one to implement rounding toward negative
296 // infinity.
298 // int64_t division truncates, so we implicitly round toward zero for positive
299 // quotients. When the absolute value of the fractional part of the remainder
300 // is >0.5, we should instead have rounded toward positive infinity, so we
301 // need to increment the quotient by one.
303 // [1]
304 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
305 if (remainder < 0 && uint64_t(std::abs(remainder)) * 2 >= uint64_t(divisor)) {
306 quotient -= 1;
308 if (remainder > 0 && uint64_t(std::abs(remainder)) * 2 > uint64_t(divisor)) {
309 quotient += 1;
311 return quotient;
315 * Compute "round half toward infinity" division `dividend / divisor`. The
316 * divisor must be a positive number.
318 inline int64_t HalfExpandDiv(int64_t dividend, int64_t divisor) {
319 MOZ_ASSERT(divisor > 0, "negative divisor not supported");
321 // NB: Division and modulo operation are fused into a single machine code
322 // instruction by the compiler.
323 int64_t quotient = dividend / divisor;
324 int64_t remainder = dividend % divisor;
326 // "Round half toward infinity" division rounds positive quotients whose
327 // remainder has a fractional part ≥0.5 toward positive infinity. And negative
328 // quotients whose remainder has a fractional part ≥0.5 toward negative
329 // infinity. See [1].
331 // int64_t division truncates, which means it rounds toward zero, so we have
332 // to increment resp. decrement the quotient when the fractional part of the
333 // remainder is ≥0.5 to round toward ±infinity.
335 // [1]
336 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
337 if (uint64_t(std::abs(remainder)) * 2 >= uint64_t(divisor)) {
338 quotient += (dividend > 0) ? 1 : -1;
340 return quotient;
344 * Compute "round half toward zero" division `dividend / divisor`. The divisor
345 * must be a positive number.
347 inline int64_t HalfTruncDiv(int64_t dividend, int64_t divisor) {
348 MOZ_ASSERT(divisor > 0, "negative divisor not supported");
350 // NB: Division and modulo operation are fused into a single machine code
351 // instruction by the compiler.
352 int64_t quotient = dividend / divisor;
353 int64_t remainder = dividend % divisor;
355 // "Round half toward zero" division rounds both positive and negative
356 // quotients whose remainder has a fractional part ≤0.5 toward zero. See [1].
358 // int64_t division truncates, so we implicitly round toward zero. When the
359 // fractional part of the remainder is >0.5, we should instead have rounded
360 // toward ±infinity, so we need to increment resp. decrement the quotient by
361 // one.
363 // [1]
364 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
365 if (uint64_t(std::abs(remainder)) * 2 > uint64_t(divisor)) {
366 quotient += (dividend > 0) ? 1 : -1;
368 return quotient;
372 * Compute "round half to even" division `dividend / divisor`. The divisor must
373 * be a positive number.
375 inline int64_t HalfEvenDiv(int64_t dividend, int64_t divisor) {
376 MOZ_ASSERT(divisor > 0, "negative divisor not supported");
378 // NB: Division and modulo operation are fused into a single machine code
379 // instruction by the compiler.
380 int64_t quotient = dividend / divisor;
381 int64_t remainder = dividend % divisor;
383 // "Round half to even" division rounds both positive and negative quotients
384 // to the nearest even integer. See [1].
386 // int64_t division truncates, so we implicitly round toward zero. When the
387 // fractional part of the remainder is 0.5 and the quotient is odd or when the
388 // fractional part of the remainder is >0.5, we should instead have rounded
389 // toward ±infinity, so we need to increment resp. decrement the quotient by
390 // one.
392 // [1]
393 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
394 if ((quotient & 1) == 1 &&
395 uint64_t(std::abs(remainder)) * 2 == uint64_t(divisor)) {
396 quotient += (dividend > 0) ? 1 : -1;
398 if (uint64_t(std::abs(remainder)) * 2 > uint64_t(divisor)) {
399 quotient += (dividend > 0) ? 1 : -1;
401 return quotient;
405 * Perform `dividend / divisor` and round the result according to the given
406 * rounding mode.
408 inline int64_t Divide(int64_t dividend, int64_t divisor,
409 TemporalRoundingMode roundingMode) {
410 switch (roundingMode) {
411 case TemporalRoundingMode::Ceil:
412 return CeilDiv(dividend, divisor);
413 case TemporalRoundingMode::Floor:
414 return FloorDiv(dividend, divisor);
415 case TemporalRoundingMode::Expand:
416 return ExpandDiv(dividend, divisor);
417 case TemporalRoundingMode::Trunc:
418 return TruncDiv(dividend, divisor);
419 case TemporalRoundingMode::HalfCeil:
420 return HalfCeilDiv(dividend, divisor);
421 case TemporalRoundingMode::HalfFloor:
422 return HalfFloorDiv(dividend, divisor);
423 case TemporalRoundingMode::HalfExpand:
424 return HalfExpandDiv(dividend, divisor);
425 case TemporalRoundingMode::HalfTrunc:
426 return HalfTruncDiv(dividend, divisor);
427 case TemporalRoundingMode::HalfEven:
428 return HalfEvenDiv(dividend, divisor);
430 MOZ_CRASH("invalid rounding mode");
434 * Compute ceiling division ⌈dividend / divisor⌉. The divisor must be a positive
435 * number.
437 constexpr Int128 CeilDiv(const Int128& dividend, const Int128& divisor) {
438 MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
440 auto [quotient, remainder] = dividend.divrem(divisor);
442 // Ceiling division rounds the quotient toward positive infinity. When the
443 // quotient is negative, this is equivalent to rounding toward zero. See [1].
445 // Int128 division truncates, so rounding toward zero for negative quotients
446 // is already covered. When there is a non-zero positive remainder, the
447 // quotient is positive and we have to increment it by one to implement
448 // rounding toward positive infinity.
450 // [1]
451 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
452 if (remainder > Int128{0}) {
453 quotient += Int128{1};
455 return quotient;
459 * Compute floor division ⌊dividend / divisor⌋. The divisor must be a positive
460 * number.
462 constexpr Int128 FloorDiv(const Int128& dividend, const Int128& divisor) {
463 MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
465 auto [quotient, remainder] = dividend.divrem(divisor);
467 // Floor division rounds the quotient toward negative infinity. When the
468 // quotient is positive, this is equivalent to rounding toward zero. See [1].
470 // Int128 division truncates, so rounding toward zero for positive quotients
471 // is already covered. When there is a non-zero negative remainder, the
472 // quotient is negative and we have to decrement it by one to implement
473 // rounding toward negative infinity.
475 // [1]
476 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
477 if (remainder < Int128{0}) {
478 quotient -= Int128{1};
480 return quotient;
484 * Compute "round toward infinity" division `dividend / divisor`. The divisor
485 * must be a positive number.
487 constexpr Int128 ExpandDiv(const Int128& dividend, const Int128& divisor) {
488 MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
490 auto [quotient, remainder] = dividend.divrem(divisor);
492 // "Round toward infinity" division rounds positive quotients toward positive
493 // infinity and negative quotients toward negative infinity. See [1].
495 // When there is a non-zero positive remainder, the quotient is positive and
496 // we have to increment it by one to implement rounding toward positive
497 // infinity. When there is a non-zero negative remainder, the quotient is
498 // negative and we have to decrement it by one to implement rounding toward
499 // negative infinity.
501 // [1]
502 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
503 if (remainder > Int128{0}) {
504 quotient += Int128{1};
506 if (remainder < Int128{0}) {
507 quotient -= Int128{1};
509 return quotient;
513 * Compute truncating division `dividend / divisor`. The divisor must be a
514 * positive number.
516 constexpr Int128 TruncDiv(const Int128& dividend, const Int128& divisor) {
517 MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
519 // Truncating division rounds both positive and negative quotients toward
520 // zero, cf. [1].
522 // Int128 division truncates, so rounding toward zero implicitly happens.
524 // [1]
525 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
526 return dividend / divisor;
530 * Compute "round half toward positive infinity" division `dividend / divisor`.
531 * The divisor must be a positive number.
533 inline Int128 HalfCeilDiv(const Int128& dividend, const Int128& divisor) {
534 MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
536 auto [quotient, remainder] = dividend.divrem(divisor);
538 // "Round half toward positive infinity" division rounds the quotient toward
539 // positive infinity when the fractional part of the remainder is ≥0.5. When
540 // the quotient is negative, this is equivalent to rounding toward zero
541 // instead of toward positive infinity. See [1].
543 // When the remainder is a non-zero positive value, the quotient is positive,
544 // too. When additionally the fractional part of the remainder is ≥0.5, we
545 // have to increment the quotient by one to implement rounding toward positive
546 // infinity.
548 // Int128 division truncates, so we implicitly round toward zero for negative
549 // quotients. When the absolute value of the fractional part of the remainder
550 // is >0.5, we should instead have rounded toward negative infinity, so we
551 // need to decrement the quotient by one.
553 // [1]
554 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
555 if (remainder > Int128{0} &&
556 Uint128(remainder.abs()) * Uint128{2} >= static_cast<Uint128>(divisor)) {
557 quotient += Int128{1};
559 if (remainder < Int128{0} &&
560 Uint128(remainder.abs()) * Uint128{2} > static_cast<Uint128>(divisor)) {
561 quotient -= Int128{1};
563 return quotient;
567 * Compute "round half toward negative infinity" division `dividend / divisor`.
568 * The divisor must be a positive number.
570 inline Int128 HalfFloorDiv(const Int128& dividend, const Int128& divisor) {
571 MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
573 auto [quotient, remainder] = dividend.divrem(divisor);
575 // "Round half toward negative infinity" division rounds the quotient toward
576 // negative infinity when the fractional part of the remainder is ≥0.5. When
577 // the quotient is positive, this is equivalent to rounding toward zero
578 // instead of toward negative infinity. See [1].
580 // When the remainder is a non-zero negative value, the quotient is negative,
581 // too. When additionally the fractional part of the remainder is ≥0.5, we
582 // have to decrement the quotient by one to implement rounding toward negative
583 // infinity.
585 // Int128 division truncates, so we implicitly round toward zero for positive
586 // quotients. When the absolute value of the fractional part of the remainder
587 // is >0.5, we should instead have rounded toward positive infinity, so we
588 // need to increment the quotient by one.
590 // [1]
591 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
592 if (remainder < Int128{0} &&
593 Uint128(remainder.abs()) * Uint128{2} >= static_cast<Uint128>(divisor)) {
594 quotient -= Int128{1};
596 if (remainder > Int128{0} &&
597 Uint128(remainder.abs()) * Uint128{2} > static_cast<Uint128>(divisor)) {
598 quotient += Int128{1};
600 return quotient;
604 * Compute "round half toward infinity" division `dividend / divisor`. The
605 * divisor must be a positive number.
607 inline Int128 HalfExpandDiv(const Int128& dividend, const Int128& divisor) {
608 MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
610 auto [quotient, remainder] = dividend.divrem(divisor);
612 // "Round half toward infinity" division rounds positive quotients whose
613 // remainder has a fractional part ≥0.5 toward positive infinity. And negative
614 // quotients whose remainder has a fractional part ≥0.5 toward negative
615 // infinity. See [1].
617 // Int128 division truncates, which means it rounds toward zero, so we have
618 // to increment resp. decrement the quotient when the fractional part of the
619 // remainder is ≥0.5 to round toward ±infinity.
621 // [1]
622 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
623 if (Uint128(remainder.abs()) * Uint128{2} >= static_cast<Uint128>(divisor)) {
624 quotient += (dividend > Int128{0}) ? Int128{1} : Int128{-1};
626 return quotient;
630 * Compute "round half toward zero" division `dividend / divisor`. The divisor
631 * must be a positive number.
633 inline Int128 HalfTruncDiv(const Int128& dividend, const Int128& divisor) {
634 MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
636 auto [quotient, remainder] = dividend.divrem(divisor);
638 // "Round half toward zero" division rounds both positive and negative
639 // quotients whose remainder has a fractional part ≤0.5 toward zero. See [1].
641 // Int128 division truncates, so we implicitly round toward zero. When the
642 // fractional part of the remainder is >0.5, we should instead have rounded
643 // toward ±infinity, so we need to increment resp. decrement the quotient by
644 // one.
646 // [1]
647 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
648 if (Uint128(remainder.abs()) * Uint128{2} > static_cast<Uint128>(divisor)) {
649 quotient += (dividend > Int128{0}) ? Int128{1} : Int128{-1};
651 return quotient;
655 * Compute "round half to even" division `dividend / divisor`. The divisor must
656 * be a positive number.
658 inline Int128 HalfEvenDiv(const Int128& dividend, const Int128& divisor) {
659 MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
661 auto [quotient, remainder] = dividend.divrem(divisor);
663 // "Round half to even" division rounds both positive and negative quotients
664 // to the nearest even integer. See [1].
666 // Int128 division truncates, so we implicitly round toward zero. When the
667 // fractional part of the remainder is 0.5 and the quotient is odd or when the
668 // fractional part of the remainder is >0.5, we should instead have rounded
669 // toward ±infinity, so we need to increment resp. decrement the quotient by
670 // one.
672 // [1]
673 // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
674 if ((quotient & Int128{1}) == Int128{1} &&
675 Uint128(remainder.abs()) * Uint128{2} == static_cast<Uint128>(divisor)) {
676 quotient += (dividend > Int128{0}) ? Int128{1} : Int128{-1};
678 if (Uint128(remainder.abs()) * Uint128{2} > static_cast<Uint128>(divisor)) {
679 quotient += (dividend > Int128{0}) ? Int128{1} : Int128{-1};
681 return quotient;
685 * Perform `dividend / divisor` and round the result according to the given
686 * rounding mode.
688 inline Int128 Divide(const Int128& dividend, const Int128& divisor,
689 TemporalRoundingMode roundingMode) {
690 switch (roundingMode) {
691 case TemporalRoundingMode::Ceil:
692 return CeilDiv(dividend, divisor);
693 case TemporalRoundingMode::Floor:
694 return FloorDiv(dividend, divisor);
695 case TemporalRoundingMode::Expand:
696 return ExpandDiv(dividend, divisor);
697 case TemporalRoundingMode::Trunc:
698 return TruncDiv(dividend, divisor);
699 case TemporalRoundingMode::HalfCeil:
700 return HalfCeilDiv(dividend, divisor);
701 case TemporalRoundingMode::HalfFloor:
702 return HalfFloorDiv(dividend, divisor);
703 case TemporalRoundingMode::HalfExpand:
704 return HalfExpandDiv(dividend, divisor);
705 case TemporalRoundingMode::HalfTrunc:
706 return HalfTruncDiv(dividend, divisor);
707 case TemporalRoundingMode::HalfEven:
708 return HalfEvenDiv(dividend, divisor);
710 MOZ_CRASH("invalid rounding mode");
713 } /* namespace js::temporal */
715 #endif /* builtin_temporal_TemporalRoundingMode_h */