tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TemporalRoundingMode.h (26634B)


      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/. */
      6 
      7 #ifndef builtin_temporal_TemporalRoundingMode_h
      8 #define builtin_temporal_TemporalRoundingMode_h
      9 
     10 #include "mozilla/Assertions.h"
     11 
     12 #include <cmath>
     13 #include <stdint.h>
     14 
     15 #include "vm/Int128.h"
     16 
     17 namespace js::temporal {
     18 
     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.
     23 
     24  // Round toward positive infinity.
     25  Ceil,
     26 
     27  // Round toward negative infinity.
     28  Floor,
     29 
     30  // Round toward infinity or round away from zero.
     31  Expand,
     32 
     33  // Round toward zero or round away from infinity.
     34  Trunc,
     35 
     36  // 2. Rounding to the nearest integer.
     37 
     38  // Round half toward positive infinity.
     39  HalfCeil,
     40 
     41  // Round half toward negative infinity.
     42  HalfFloor,
     43 
     44  // Round half toward infinity or round half away from zero.
     45  HalfExpand,
     46 
     47  // Round half toward zero or round half away from infinity.
     48  HalfTrunc,
     49 
     50  // Round half to even.
     51  HalfEven,
     52 };
     53 
     54 /**
     55 * NegateRoundingMode ( roundingMode )
     56 */
     57 constexpr auto NegateRoundingMode(TemporalRoundingMode roundingMode) {
     58  // Steps 1-5.
     59  switch (roundingMode) {
     60    case TemporalRoundingMode::Ceil:
     61      return TemporalRoundingMode::Floor;
     62 
     63    case TemporalRoundingMode::Floor:
     64      return TemporalRoundingMode::Ceil;
     65 
     66    case TemporalRoundingMode::HalfCeil:
     67      return TemporalRoundingMode::HalfFloor;
     68 
     69    case TemporalRoundingMode::HalfFloor:
     70      return TemporalRoundingMode::HalfCeil;
     71 
     72    case TemporalRoundingMode::Expand:
     73    case TemporalRoundingMode::Trunc:
     74    case TemporalRoundingMode::HalfExpand:
     75    case TemporalRoundingMode::HalfTrunc:
     76    case TemporalRoundingMode::HalfEven:
     77      return roundingMode;
     78  }
     79  MOZ_CRASH("invalid rounding mode");
     80 }
     81 
     82 /**
     83 * Adjust the rounding mode to round negative values in the same direction as
     84 * positive values.
     85 */
     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;
     97 
     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;
    103 
    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;
    109 
    110    case TemporalRoundingMode::HalfExpand:
    111      // Adjust the rounding mode to Half-Ceil, similar to the Expand case.
    112      return TemporalRoundingMode::HalfCeil;
    113 
    114    case TemporalRoundingMode::HalfTrunc:
    115      // Adjust the rounding mode to Half-Floor, similar to the Trunc case.
    116      return TemporalRoundingMode::HalfFloor;
    117  }
    118  MOZ_CRASH("unexpected rounding mode");
    119 }
    120 
    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.
    126 //
    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.
    130 //
    131 // [1] https://tc39.es/ecma262/#mathematical-value
    132 
    133 /**
    134 * Compute ceiling division ⌈dividend / divisor⌉. The divisor must be a positive
    135 * number.
    136 */
    137 constexpr int64_t CeilDiv(int64_t dividend, int64_t divisor) {
    138  MOZ_ASSERT(divisor > 0, "negative divisor not supported");
    139 
    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;
    144 
    145  // Ceiling division rounds the quotient toward positive infinity. When the
    146  // quotient is negative, this is equivalent to rounding toward zero. See [1].
    147  //
    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.
    152  //
    153  // [1]
    154  // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
    155  if (remainder > 0) {
    156    quotient += 1;
    157  }
    158  return quotient;
    159 }
    160 
    161 /**
    162 * Compute floor division ⌊dividend / divisor⌋. The divisor must be a positive
    163 * number.
    164 */
    165 constexpr int64_t FloorDiv(int64_t dividend, int64_t divisor) {
    166  MOZ_ASSERT(divisor > 0, "negative divisor not supported");
    167 
    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;
    172 
    173  // Floor division rounds the quotient toward negative infinity. When the
    174  // quotient is positive, this is equivalent to rounding toward zero. See [1].
    175  //
    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.
    180  //
    181  // [1]
    182  // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
    183  if (remainder < 0) {
    184    quotient -= 1;
    185  }
    186  return quotient;
    187 }
    188 
    189 /**
    190 * Compute "round toward infinity" division `dividend / divisor`. The divisor
    191 * must be a positive number.
    192 */
    193 constexpr int64_t ExpandDiv(int64_t dividend, int64_t divisor) {
    194  MOZ_ASSERT(divisor > 0, "negative divisor not supported");
    195 
    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;
    200 
    201  // "Round toward infinity" division rounds positive quotients toward positive
    202  // infinity and negative quotients toward negative infinity. See [1].
    203  //
    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.
    209  //
    210  // [1]
    211  // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
    212  if (remainder > 0) {
    213    quotient += 1;
    214  }
    215  if (remainder < 0) {
    216    quotient -= 1;
    217  }
    218  return quotient;
    219 }
    220 
    221 /**
    222 * Compute truncating division `dividend / divisor`. The divisor must be a
    223 * positive number.
    224 */
    225 constexpr int64_t TruncDiv(int64_t dividend, int64_t divisor) {
    226  MOZ_ASSERT(divisor > 0, "negative divisor not supported");
    227 
    228  // Truncating division rounds both positive and negative quotients toward
    229  // zero, cf. [1].
    230  //
    231  // int64_t division truncates, so rounding toward zero implicitly happens.
    232  //
    233  // [1]
    234  // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
    235  return dividend / divisor;
    236 }
    237 
    238 /**
    239 * Compute "round half toward positive infinity" division `dividend / divisor`.
    240 * The divisor must be a positive number.
    241 */
    242 inline int64_t HalfCeilDiv(int64_t dividend, int64_t divisor) {
    243  MOZ_ASSERT(divisor > 0, "negative divisor not supported");
    244 
    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;
    249 
    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].
    254  //
    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.
    259  //
    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.
    264  //
    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;
    269  }
    270  if (remainder < 0 && uint64_t(std::abs(remainder)) * 2 > uint64_t(divisor)) {
    271    quotient -= 1;
    272  }
    273  return quotient;
    274 }
    275 
    276 /**
    277 * Compute "round half toward negative infinity" division `dividend / divisor`.
    278 * The divisor must be a positive number.
    279 */
    280 inline int64_t HalfFloorDiv(int64_t dividend, int64_t divisor) {
    281  MOZ_ASSERT(divisor > 0, "negative divisor not supported");
    282 
    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;
    287 
    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].
    292  //
    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.
    297  //
    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.
    302  //
    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;
    307  }
    308  if (remainder > 0 && uint64_t(std::abs(remainder)) * 2 > uint64_t(divisor)) {
    309    quotient += 1;
    310  }
    311  return quotient;
    312 }
    313 
    314 /**
    315 * Compute "round half toward infinity" division `dividend / divisor`. The
    316 * divisor must be a positive number.
    317 */
    318 inline int64_t HalfExpandDiv(int64_t dividend, int64_t divisor) {
    319  MOZ_ASSERT(divisor > 0, "negative divisor not supported");
    320 
    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;
    325 
    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].
    330  //
    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.
    334  //
    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;
    339  }
    340  return quotient;
    341 }
    342 
    343 /**
    344 * Compute "round half toward zero" division `dividend / divisor`. The divisor
    345 * must be a positive number.
    346 */
    347 inline int64_t HalfTruncDiv(int64_t dividend, int64_t divisor) {
    348  MOZ_ASSERT(divisor > 0, "negative divisor not supported");
    349 
    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;
    354 
    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].
    357  //
    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.
    362  //
    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;
    367  }
    368  return quotient;
    369 }
    370 
    371 /**
    372 * Compute "round half to even" division `dividend / divisor`. The divisor must
    373 * be a positive number.
    374 */
    375 inline int64_t HalfEvenDiv(int64_t dividend, int64_t divisor) {
    376  MOZ_ASSERT(divisor > 0, "negative divisor not supported");
    377 
    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;
    382 
    383  // "Round half to even" division rounds both positive and negative quotients
    384  // to the nearest even integer. See [1].
    385  //
    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.
    391  //
    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;
    397  }
    398  if (uint64_t(std::abs(remainder)) * 2 > uint64_t(divisor)) {
    399    quotient += (dividend > 0) ? 1 : -1;
    400  }
    401  return quotient;
    402 }
    403 
    404 /**
    405 * Perform `dividend / divisor` and round the result according to the given
    406 * rounding mode.
    407 */
    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);
    429  }
    430  MOZ_CRASH("invalid rounding mode");
    431 }
    432 
    433 /**
    434 * Compute ceiling division ⌈dividend / divisor⌉. The divisor must be a positive
    435 * number.
    436 */
    437 inline Int128 CeilDiv(const Int128& dividend, const Int128& divisor) {
    438  MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
    439 
    440  auto [quotient, remainder] = dividend.divrem(divisor);
    441 
    442  // Ceiling division rounds the quotient toward positive infinity. When the
    443  // quotient is negative, this is equivalent to rounding toward zero. See [1].
    444  //
    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.
    449  //
    450  // [1]
    451  // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
    452  if (remainder > Int128{0}) {
    453    quotient += Int128{1};
    454  }
    455  return quotient;
    456 }
    457 
    458 /**
    459 * Compute floor division ⌊dividend / divisor⌋. The divisor must be a positive
    460 * number.
    461 */
    462 inline Int128 FloorDiv(const Int128& dividend, const Int128& divisor) {
    463  MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
    464 
    465  auto [quotient, remainder] = dividend.divrem(divisor);
    466 
    467  // Floor division rounds the quotient toward negative infinity. When the
    468  // quotient is positive, this is equivalent to rounding toward zero. See [1].
    469  //
    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.
    474  //
    475  // [1]
    476  // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
    477  if (remainder < Int128{0}) {
    478    quotient -= Int128{1};
    479  }
    480  return quotient;
    481 }
    482 
    483 /**
    484 * Compute "round toward infinity" division `dividend / divisor`. The divisor
    485 * must be a positive number.
    486 */
    487 inline Int128 ExpandDiv(const Int128& dividend, const Int128& divisor) {
    488  MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
    489 
    490  auto [quotient, remainder] = dividend.divrem(divisor);
    491 
    492  // "Round toward infinity" division rounds positive quotients toward positive
    493  // infinity and negative quotients toward negative infinity. See [1].
    494  //
    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.
    500  //
    501  // [1]
    502  // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
    503  if (remainder > Int128{0}) {
    504    quotient += Int128{1};
    505  }
    506  if (remainder < Int128{0}) {
    507    quotient -= Int128{1};
    508  }
    509  return quotient;
    510 }
    511 
    512 /**
    513 * Compute truncating division `dividend / divisor`. The divisor must be a
    514 * positive number.
    515 */
    516 inline Int128 TruncDiv(const Int128& dividend, const Int128& divisor) {
    517  MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
    518 
    519  // Truncating division rounds both positive and negative quotients toward
    520  // zero, cf. [1].
    521  //
    522  // Int128 division truncates, so rounding toward zero implicitly happens.
    523  //
    524  // [1]
    525  // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
    526  return dividend / divisor;
    527 }
    528 
    529 /**
    530 * Compute "round half toward positive infinity" division `dividend / divisor`.
    531 * The divisor must be a positive number.
    532 */
    533 inline Int128 HalfCeilDiv(const Int128& dividend, const Int128& divisor) {
    534  MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
    535 
    536  auto [quotient, remainder] = dividend.divrem(divisor);
    537 
    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].
    542  //
    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.
    547  //
    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.
    552  //
    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};
    558  }
    559  if (remainder < Int128{0} &&
    560      Uint128(remainder.abs()) * Uint128{2} > static_cast<Uint128>(divisor)) {
    561    quotient -= Int128{1};
    562  }
    563  return quotient;
    564 }
    565 
    566 /**
    567 * Compute "round half toward negative infinity" division `dividend / divisor`.
    568 * The divisor must be a positive number.
    569 */
    570 inline Int128 HalfFloorDiv(const Int128& dividend, const Int128& divisor) {
    571  MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
    572 
    573  auto [quotient, remainder] = dividend.divrem(divisor);
    574 
    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].
    579  //
    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.
    584  //
    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.
    589  //
    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};
    595  }
    596  if (remainder > Int128{0} &&
    597      Uint128(remainder.abs()) * Uint128{2} > static_cast<Uint128>(divisor)) {
    598    quotient += Int128{1};
    599  }
    600  return quotient;
    601 }
    602 
    603 /**
    604 * Compute "round half toward infinity" division `dividend / divisor`. The
    605 * divisor must be a positive number.
    606 */
    607 inline Int128 HalfExpandDiv(const Int128& dividend, const Int128& divisor) {
    608  MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
    609 
    610  auto [quotient, remainder] = dividend.divrem(divisor);
    611 
    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].
    616  //
    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.
    620  //
    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};
    625  }
    626  return quotient;
    627 }
    628 
    629 /**
    630 * Compute "round half toward zero" division `dividend / divisor`. The divisor
    631 * must be a positive number.
    632 */
    633 inline Int128 HalfTruncDiv(const Int128& dividend, const Int128& divisor) {
    634  MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
    635 
    636  auto [quotient, remainder] = dividend.divrem(divisor);
    637 
    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].
    640  //
    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.
    645  //
    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};
    650  }
    651  return quotient;
    652 }
    653 
    654 /**
    655 * Compute "round half to even" division `dividend / divisor`. The divisor must
    656 * be a positive number.
    657 */
    658 inline Int128 HalfEvenDiv(const Int128& dividend, const Int128& divisor) {
    659  MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
    660 
    661  auto [quotient, remainder] = dividend.divrem(divisor);
    662 
    663  // "Round half to even" division rounds both positive and negative quotients
    664  // to the nearest even integer. See [1].
    665  //
    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.
    671  //
    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};
    677  }
    678  if (Uint128(remainder.abs()) * Uint128{2} > static_cast<Uint128>(divisor)) {
    679    quotient += (dividend > Int128{0}) ? Int128{1} : Int128{-1};
    680  }
    681  return quotient;
    682 }
    683 
    684 /**
    685 * Perform `dividend / divisor` and round the result according to the given
    686 * rounding mode.
    687 */
    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);
    709  }
    710  MOZ_CRASH("invalid rounding mode");
    711 }
    712 
    713 } /* namespace js::temporal */
    714 
    715 #endif /* builtin_temporal_TemporalRoundingMode_h */