#### Source code

#### Revision control

#### Copy as Markdown

#### Other Tools

```
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
```

```
* vim: set ts=8 sts=2 et sw=2 tw=80:
```

```
* This Source Code Form is subject to the terms of the Mozilla Public
```

```
* License, v. 2.0. If a copy of the MPL was not distributed with this
```

```
```

```
#ifndef builtin_temporal_TemporalRoundingMode_h
```

```
#define builtin_temporal_TemporalRoundingMode_h
```

```
```

```
#include "mozilla/Assertions.h"
```

```
```

```
#include <cmath>
```

```
#include <stdint.h>
```

```
```

```
namespace js::temporal {
```

```
```

```
// Overview of integer rounding modes is available at
```

```
enum class TemporalRoundingMode {
```

```
// 1. Directed rounding to an integer.
```

```
```

```
// Round toward positive infinity.
```

```
Ceil,
```

```
```

```
// Round toward negative infinity.
```

```
Floor,
```

```
```

```
// Round toward infinity or round away from zero.
```

```
Expand,
```

```
```

```
// Round toward zero or round away from infinity.
```

```
Trunc,
```

```
```

```
// 2. Rounding to the nearest integer.
```

```
```

```
// Round half toward positive infinity.
```

```
HalfCeil,
```

```
```

```
// Round half toward negative infinity.
```

```
HalfFloor,
```

```
```

```
// Round half toward infinity or round half away from zero.
```

```
HalfExpand,
```

```
```

```
// Round half toward zero or round half away from infinity.
```

```
HalfTrunc,
```

```
```

```
// Round half to even.
```

```
HalfEven,
```

```
};
```

```
```

```
/**
```

```
* NegateTemporalRoundingMode ( roundingMode )
```

```
*/
```

```
constexpr auto NegateTemporalRoundingMode(TemporalRoundingMode roundingMode) {
```

```
// Steps 1-5.
```

```
switch (roundingMode) {
```

```
case TemporalRoundingMode::Ceil:
```

```
return TemporalRoundingMode::Floor;
```

```
```

```
case TemporalRoundingMode::Floor:
```

```
return TemporalRoundingMode::Ceil;
```

```
```

```
case TemporalRoundingMode::HalfCeil:
```

```
return TemporalRoundingMode::HalfFloor;
```

```
```

```
case TemporalRoundingMode::HalfFloor:
```

```
return TemporalRoundingMode::HalfCeil;
```

```
```

```
case TemporalRoundingMode::Expand:
```

```
case TemporalRoundingMode::Trunc:
```

```
case TemporalRoundingMode::HalfExpand:
```

```
case TemporalRoundingMode::HalfTrunc:
```

```
case TemporalRoundingMode::HalfEven:
```

```
return roundingMode;
```

```
}
```

```
MOZ_CRASH("invalid rounding mode");
```

```
}
```

```
```

```
/**
```

```
* Adjust the rounding mode to round negative values in the same direction as
```

```
* positive values.
```

```
*/
```

```
constexpr auto ToPositiveRoundingMode(TemporalRoundingMode roundingMode) {
```

```
switch (roundingMode) {
```

```
case TemporalRoundingMode::Ceil:
```

```
case TemporalRoundingMode::Floor:
```

```
case TemporalRoundingMode::HalfCeil:
```

```
case TemporalRoundingMode::HalfFloor:
```

```
case TemporalRoundingMode::HalfEven:
```

```
// (Half-)Ceil/Floor round toward the same infinity for negative and
```

```
// positive values, so the rounding mode doesn't need to be adjusted. The
```

```
// same applies for half-even rounding.
```

```
return roundingMode;
```

```
```

```
case TemporalRoundingMode::Expand:
```

```
// Expand rounds positive values toward +infinity, but negative values
```

```
// toward -infinity. Adjust the rounding mode to Ceil to round negative
```

```
// values in the same direction as positive values.
```

```
return TemporalRoundingMode::Ceil;
```

```
```

```
case TemporalRoundingMode::Trunc:
```

```
// Truncation rounds positive values down toward zero, but negative values
```

```
// up toward zero. Adjust the rounding mode to Floor to round negative
```

```
// values in the same direction as positive values.
```

```
return TemporalRoundingMode::Floor;
```

```
```

```
case TemporalRoundingMode::HalfExpand:
```

```
// Adjust the rounding mode to Half-Ceil, similar to the Expand case.
```

```
return TemporalRoundingMode::HalfCeil;
```

```
```

```
case TemporalRoundingMode::HalfTrunc:
```

```
// Adjust the rounding mode to Half-Floor, similar to the Trunc case.
```

```
return TemporalRoundingMode::HalfFloor;
```

```
}
```

```
MOZ_CRASH("unexpected rounding mode");
```

```
}
```

```
```

```
// Temporal performs division on "mathematical values" [1] with implies using
```

```
// infinite precision. This rules out using IEE-754 floating point types like
```

```
// `double`. It also means we can't implement the algorithms from the
```

```
// specification verbatim, but instead have to translate them into equivalent
```

```
// operations.
```

```
//
```

```
// Throughout the following division functions, the divisor is required to be
```

```
// positive. This allows to simplify the implementation, because it ensures
```

```
// non-zero quotient and remainder values have the same sign as the dividend.
```

```
//
```

```
```

```
/**
```

```
* Compute ceiling division ⌈dividend / divisor⌉. The divisor must be a positive
```

```
* number.
```

```
*/
```

```
constexpr int64_t CeilDiv(int64_t dividend, int64_t divisor) {
```

```
MOZ_ASSERT(divisor > 0, "negative divisor not supported");
```

```
```

```
// NB: Division and modulo operation are fused into a single machine code
```

```
// instruction by the compiler.
```

```
int64_t quotient = dividend / divisor;
```

```
int64_t remainder = dividend % divisor;
```

```
```

```
// Ceiling division rounds the quotient toward positive infinity. When the
```

```
// quotient is negative, this is equivalent to rounding toward zero. See [1].
```

```
//
```

```
// int64_t division truncates, so rounding toward zero for negative quotients
```

```
// is already covered. When there is a non-zero positive remainder, the
```

```
// quotient is positive and we have to increment it by one to implement
```

```
// rounding toward positive infinity.
```

```
//
```

```
// [1]
```

```
if (remainder > 0) {
```

```
quotient += 1;
```

```
}
```

```
return quotient;
```

```
}
```

```
```

```
/**
```

```
* Compute floor division ⌊dividend / divisor⌋. The divisor must be a positive
```

```
* number.
```

```
*/
```

```
constexpr int64_t FloorDiv(int64_t dividend, int64_t divisor) {
```

```
MOZ_ASSERT(divisor > 0, "negative divisor not supported");
```

```
```

```
// NB: Division and modulo operation are fused into a single machine code
```

```
// instruction by the compiler.
```

```
int64_t quotient = dividend / divisor;
```

```
int64_t remainder = dividend % divisor;
```

```
```

```
// Floor division rounds the quotient toward negative infinity. When the
```

```
// quotient is positive, this is equivalent to rounding toward zero. See [1].
```

```
//
```

```
// int64_t division truncates, so rounding toward zero for positive quotients
```

```
// is already covered. When there is a non-zero negative remainder, the
```

```
// quotient is negative and we have to decrement it by one to implement
```

```
// rounding toward negative infinity.
```

```
//
```

```
// [1]
```

```
if (remainder < 0) {
```

```
quotient -= 1;
```

```
}
```

```
return quotient;
```

```
}
```

```
```

```
/**
```

```
* Compute "round toward infinity" division `dividend / divisor`. The divisor
```

```
* must be a positive number.
```

```
*/
```

```
constexpr int64_t ExpandDiv(int64_t dividend, int64_t divisor) {
```

```
MOZ_ASSERT(divisor > 0, "negative divisor not supported");
```

```
```

```
// NB: Division and modulo operation are fused into a single machine code
```

```
// instruction by the compiler.
```

```
int64_t quotient = dividend / divisor;
```

```
int64_t remainder = dividend % divisor;
```

```
```

```
// "Round toward infinity" division rounds positive quotients toward positive
```

```
// infinity and negative quotients toward negative infinity. See [1].
```

```
//
```

```
// When there is a non-zero positive remainder, the quotient is positive and
```

```
// we have to increment it by one to implement rounding toward positive
```

```
// infinity. When there is a non-zero negative remainder, the quotient is
```

```
// negative and we have to decrement it by one to implement rounding toward
```

```
// negative infinity.
```

```
//
```

```
// [1]
```

```
if (remainder > 0) {
```

```
quotient += 1;
```

```
}
```

```
if (remainder < 0) {
```

```
quotient -= 1;
```

```
}
```

```
return quotient;
```

```
}
```

```
```

```
/**
```

```
* Compute truncating division `dividend / divisor`. The divisor must be a
```

```
* positive number.
```

```
*/
```

```
constexpr int64_t TruncDiv(int64_t dividend, int64_t divisor) {
```

```
MOZ_ASSERT(divisor > 0, "negative divisor not supported");
```

```
```

```
// Truncating division rounds both positive and negative quotients toward
```

```
// zero, cf. [1].
```

```
//
```

```
// int64_t division truncates, so rounding toward zero implicitly happens.
```

```
//
```

```
// [1]
```

```
return dividend / divisor;
```

```
}
```

```
```

```
/**
```

```
* Compute "round half toward positive infinity" division `dividend / divisor`.
```

```
* The divisor must be a positive number.
```

```
*/
```

```
inline int64_t HalfCeilDiv(int64_t dividend, int64_t divisor) {
```

```
MOZ_ASSERT(divisor > 0, "negative divisor not supported");
```

```
```

```
// NB: Division and modulo operation are fused into a single machine code
```

```
// instruction by the compiler.
```

```
int64_t quotient = dividend / divisor;
```

```
int64_t remainder = dividend % divisor;
```

```
```

```
// "Round half toward positive infinity" division rounds the quotient toward
```

```
// positive infinity when the fractional part of the remainder is ≥0.5. When
```

```
// the quotient is negative, this is equivalent to rounding toward zero
```

```
// instead of toward positive infinity. See [1].
```

```
//
```

```
// When the remainder is a non-zero positive value, the quotient is positive,
```

```
// too. When additionally the fractional part of the remainder is ≥0.5, we
```

```
// have to increment the quotient by one to implement rounding toward positive
```

```
// infinity.
```

```
//
```

```
// int64_t division truncates, so we implicitly round toward zero for negative
```

```
// quotients. When the absolute value of the fractional part of the remainder
```

```
// is >0.5, we should instead have rounded toward negative infinity, so we
```

```
// need to decrement the quotient by one.
```

```
//
```

```
// [1]
```

```
if (remainder > 0 && uint64_t(std::abs(remainder)) * 2 >= uint64_t(divisor)) {
```

```
quotient += 1;
```

```
}
```

```
if (remainder < 0 && uint64_t(std::abs(remainder)) * 2 > uint64_t(divisor)) {
```

```
quotient -= 1;
```

```
}
```

```
return quotient;
```

```
}
```

```
```

```
/**
```

```
* Compute "round half toward negative infinity" division `dividend / divisor`.
```

```
* The divisor must be a positive number.
```

```
*/
```

```
inline int64_t HalfFloorDiv(int64_t dividend, int64_t divisor) {
```

```
MOZ_ASSERT(divisor > 0, "negative divisor not supported");
```

```
```

```
// NB: Division and modulo operation are fused into a single machine code
```

```
// instruction by the compiler.
```

```
int64_t quotient = dividend / divisor;
```

```
int64_t remainder = dividend % divisor;
```

```
```

```
// "Round half toward negative infinity" division rounds the quotient toward
```

```
// negative infinity when the fractional part of the remainder is ≥0.5. When
```

```
// the quotient is positive, this is equivalent to rounding toward zero
```

```
// instead of toward negative infinity. See [1].
```

```
//
```

```
// When the remainder is a non-zero negative value, the quotient is negative,
```

```
// too. When additionally the fractional part of the remainder is ≥0.5, we
```

```
// have to decrement the quotient by one to implement rounding toward negative
```

```
// infinity.
```

```
//
```

```
// int64_t division truncates, so we implicitly round toward zero for positive
```

```
// quotients. When the absolute value of the fractional part of the remainder
```

```
// is >0.5, we should instead have rounded toward positive infinity, so we
```

```
// need to increment the quotient by one.
```

```
//
```

```
// [1]
```

```
if (remainder < 0 && uint64_t(std::abs(remainder)) * 2 >= uint64_t(divisor)) {
```

```
quotient -= 1;
```

```
}
```

```
if (remainder > 0 && uint64_t(std::abs(remainder)) * 2 > uint64_t(divisor)) {
```

```
quotient += 1;
```

```
}
```

```
return quotient;
```

```
}
```

```
```

```
/**
```

```
* Compute "round half toward infinity" division `dividend / divisor`. The
```

```
* divisor must be a positive number.
```

```
*/
```

```
inline int64_t HalfExpandDiv(int64_t dividend, int64_t divisor) {
```

```
MOZ_ASSERT(divisor > 0, "negative divisor not supported");
```

```
```

```
// NB: Division and modulo operation are fused into a single machine code
```

```
// instruction by the compiler.
```

```
int64_t quotient = dividend / divisor;
```

```
int64_t remainder = dividend % divisor;
```

```
```

```
// "Round half toward infinity" division rounds positive quotients whose
```

```
// remainder has a fractional part ≥0.5 toward positive infinity. And negative
```

```
// quotients whose remainder has a fractional part ≥0.5 toward negative
```

```
// infinity. See [1].
```

```
//
```

```
// int64_t division truncates, which means it rounds toward zero, so we have
```

```
// to increment resp. decrement the quotient when the fractional part of the
```

```
// remainder is ≥0.5 to round toward ±infinity.
```

```
//
```

```
// [1]
```

```
if (uint64_t(std::abs(remainder)) * 2 >= uint64_t(divisor)) {
```

```
quotient += (dividend > 0) ? 1 : -1;
```

```
}
```

```
return quotient;
```

```
}
```

```
```

```
/**
```

```
* Compute "round half toward zero" division `dividend / divisor`. The divisor
```

```
* must be a positive number.
```

```
*/
```

```
inline int64_t HalfTruncDiv(int64_t dividend, int64_t divisor) {
```

```
MOZ_ASSERT(divisor > 0, "negative divisor not supported");
```

```
```

```
// NB: Division and modulo operation are fused into a single machine code
```

```
// instruction by the compiler.
```

```
int64_t quotient = dividend / divisor;
```

```
int64_t remainder = dividend % divisor;
```

```
```

```
// "Round half toward zero" division rounds both positive and negative
```

```
// quotients whose remainder has a fractional part ≤0.5 toward zero. See [1].
```

```
//
```

```
// int64_t division truncates, so we implicitly round toward zero. When the
```

```
// fractional part of the remainder is >0.5, we should instead have rounded
```

```
// toward ±infinity, so we need to increment resp. decrement the quotient by
```

```
// one.
```

```
//
```

```
// [1]
```

```
if (uint64_t(std::abs(remainder)) * 2 > uint64_t(divisor)) {
```

```
quotient += (dividend > 0) ? 1 : -1;
```

```
}
```

```
return quotient;
```

```
}
```

```
```

```
/**
```

```
* Compute "round half to even" division `dividend / divisor`. The divisor must
```

```
* be a positive number.
```

```
*/
```

```
inline int64_t HalfEvenDiv(int64_t dividend, int64_t divisor) {
```

```
MOZ_ASSERT(divisor > 0, "negative divisor not supported");
```

```
```

```
// NB: Division and modulo operation are fused into a single machine code
```

```
// instruction by the compiler.
```

```
int64_t quotient = dividend / divisor;
```

```
int64_t remainder = dividend % divisor;
```

```
```

```
// "Round half to even" division rounds both positive and negative quotients
```

```
// to the nearest even integer. See [1].
```

```
//
```

```
// int64_t division truncates, so we implicitly round toward zero. When the
```

```
// fractional part of the remainder is 0.5 and the quotient is odd or when the
```

```
// fractional part of the remainder is >0.5, we should instead have rounded
```

```
// toward ±infinity, so we need to increment resp. decrement the quotient by
```

```
// one.
```

```
//
```

```
// [1]
```

```
if ((quotient & 1) == 1 &&
```

```
uint64_t(std::abs(remainder)) * 2 == uint64_t(divisor)) {
```

```
quotient += (dividend > 0) ? 1 : -1;
```

```
}
```

```
if (uint64_t(std::abs(remainder)) * 2 > uint64_t(divisor)) {
```

```
quotient += (dividend > 0) ? 1 : -1;
```

```
}
```

```
return quotient;
```

```
}
```

```
```

```
/**
```

```
* Perform `dividend / divisor` and round the result according to the given
```

```
* rounding mode.
```

```
*/
```

```
inline int64_t Divide(int64_t dividend, int64_t divisor,
```

```
TemporalRoundingMode roundingMode) {
```

```
switch (roundingMode) {
```

```
case TemporalRoundingMode::Ceil:
```

```
return CeilDiv(dividend, divisor);
```

```
case TemporalRoundingMode::Floor:
```

```
return FloorDiv(dividend, divisor);
```

```
case TemporalRoundingMode::Expand:
```

```
return ExpandDiv(dividend, divisor);
```

```
case TemporalRoundingMode::Trunc:
```

```
return TruncDiv(dividend, divisor);
```

```
case TemporalRoundingMode::HalfCeil:
```

```
return HalfCeilDiv(dividend, divisor);
```

```
case TemporalRoundingMode::HalfFloor:
```

```
return HalfFloorDiv(dividend, divisor);
```

```
case TemporalRoundingMode::HalfExpand:
```

```
return HalfExpandDiv(dividend, divisor);
```

```
case TemporalRoundingMode::HalfTrunc:
```

```
return HalfTruncDiv(dividend, divisor);
```

```
case TemporalRoundingMode::HalfEven:
```

```
return HalfEvenDiv(dividend, divisor);
```

```
}
```

```
MOZ_CRASH("invalid rounding mode");
```

```
}
```

```
```

```
} /* namespace js::temporal */
```

```
```

```
#endif /* builtin_temporal_TemporalRoundingMode_h */
```