summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/temporal/TemporalRoundingMode.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
commit8dd16259287f58f9273002717ec4d27e97127719 (patch)
tree3863e62a53829a84037444beab3abd4ed9dfc7d0 /js/src/builtin/temporal/TemporalRoundingMode.h
parentReleasing progress-linux version 126.0.1-1~progress7.99u1. (diff)
downloadfirefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz
firefox-8dd16259287f58f9273002717ec4d27e97127719.zip
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/builtin/temporal/TemporalRoundingMode.h')
-rw-r--r--js/src/builtin/temporal/TemporalRoundingMode.h282
1 files changed, 282 insertions, 0 deletions
diff --git a/js/src/builtin/temporal/TemporalRoundingMode.h b/js/src/builtin/temporal/TemporalRoundingMode.h
index 91ef758fc6..23d3996d09 100644
--- a/js/src/builtin/temporal/TemporalRoundingMode.h
+++ b/js/src/builtin/temporal/TemporalRoundingMode.h
@@ -12,6 +12,8 @@
#include <cmath>
#include <stdint.h>
+#include "builtin/temporal/Int128.h"
+
namespace js::temporal {
// Overview of integer rounding modes is available at
@@ -428,6 +430,286 @@ inline int64_t Divide(int64_t dividend, int64_t divisor,
MOZ_CRASH("invalid rounding mode");
}
+/**
+ * Compute ceiling division ⌈dividend / divisor⌉. The divisor must be a positive
+ * number.
+ */
+constexpr Int128 CeilDiv(const Int128& dividend, const Int128& divisor) {
+ MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
+
+ auto [quotient, remainder] = dividend.divrem(divisor);
+
+ // Ceiling division rounds the quotient toward positive infinity. When the
+ // quotient is negative, this is equivalent to rounding toward zero. See [1].
+ //
+ // Int128 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]
+ // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
+ if (remainder > Int128{0}) {
+ quotient += Int128{1};
+ }
+ return quotient;
+}
+
+/**
+ * Compute floor division ⌊dividend / divisor⌋. The divisor must be a positive
+ * number.
+ */
+constexpr Int128 FloorDiv(const Int128& dividend, const Int128& divisor) {
+ MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
+
+ auto [quotient, remainder] = dividend.divrem(divisor);
+
+ // Floor division rounds the quotient toward negative infinity. When the
+ // quotient is positive, this is equivalent to rounding toward zero. See [1].
+ //
+ // Int128 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]
+ // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
+ if (remainder < Int128{0}) {
+ quotient -= Int128{1};
+ }
+ return quotient;
+}
+
+/**
+ * Compute "round toward infinity" division `dividend / divisor`. The divisor
+ * must be a positive number.
+ */
+constexpr Int128 ExpandDiv(const Int128& dividend, const Int128& divisor) {
+ MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
+
+ auto [quotient, remainder] = dividend.divrem(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]
+ // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
+ if (remainder > Int128{0}) {
+ quotient += Int128{1};
+ }
+ if (remainder < Int128{0}) {
+ quotient -= Int128{1};
+ }
+ return quotient;
+}
+
+/**
+ * Compute truncating division `dividend / divisor`. The divisor must be a
+ * positive number.
+ */
+constexpr Int128 TruncDiv(const Int128& dividend, const Int128& divisor) {
+ MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
+
+ // Truncating division rounds both positive and negative quotients toward
+ // zero, cf. [1].
+ //
+ // Int128 division truncates, so rounding toward zero implicitly happens.
+ //
+ // [1]
+ // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
+ return dividend / divisor;
+}
+
+/**
+ * Compute "round half toward positive infinity" division `dividend / divisor`.
+ * The divisor must be a positive number.
+ */
+inline Int128 HalfCeilDiv(const Int128& dividend, const Int128& divisor) {
+ MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
+
+ auto [quotient, remainder] = dividend.divrem(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.
+ //
+ // Int128 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]
+ // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
+ if (remainder > Int128{0} &&
+ Uint128(remainder.abs()) * Uint128{2} >= static_cast<Uint128>(divisor)) {
+ quotient += Int128{1};
+ }
+ if (remainder < Int128{0} &&
+ Uint128(remainder.abs()) * Uint128{2} > static_cast<Uint128>(divisor)) {
+ quotient -= Int128{1};
+ }
+ return quotient;
+}
+
+/**
+ * Compute "round half toward negative infinity" division `dividend / divisor`.
+ * The divisor must be a positive number.
+ */
+inline Int128 HalfFloorDiv(const Int128& dividend, const Int128& divisor) {
+ MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
+
+ auto [quotient, remainder] = dividend.divrem(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.
+ //
+ // Int128 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]
+ // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
+ if (remainder < Int128{0} &&
+ Uint128(remainder.abs()) * Uint128{2} >= static_cast<Uint128>(divisor)) {
+ quotient -= Int128{1};
+ }
+ if (remainder > Int128{0} &&
+ Uint128(remainder.abs()) * Uint128{2} > static_cast<Uint128>(divisor)) {
+ quotient += Int128{1};
+ }
+ return quotient;
+}
+
+/**
+ * Compute "round half toward infinity" division `dividend / divisor`. The
+ * divisor must be a positive number.
+ */
+inline Int128 HalfExpandDiv(const Int128& dividend, const Int128& divisor) {
+ MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
+
+ auto [quotient, remainder] = dividend.divrem(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].
+ //
+ // Int128 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]
+ // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
+ if (Uint128(remainder.abs()) * Uint128{2} >= static_cast<Uint128>(divisor)) {
+ quotient += (dividend > Int128{0}) ? Int128{1} : Int128{-1};
+ }
+ return quotient;
+}
+
+/**
+ * Compute "round half toward zero" division `dividend / divisor`. The divisor
+ * must be a positive number.
+ */
+inline Int128 HalfTruncDiv(const Int128& dividend, const Int128& divisor) {
+ MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
+
+ auto [quotient, remainder] = dividend.divrem(divisor);
+
+ // "Round half toward zero" division rounds both positive and negative
+ // quotients whose remainder has a fractional part ≤0.5 toward zero. See [1].
+ //
+ // Int128 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]
+ // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
+ if (Uint128(remainder.abs()) * Uint128{2} > static_cast<Uint128>(divisor)) {
+ quotient += (dividend > Int128{0}) ? Int128{1} : Int128{-1};
+ }
+ return quotient;
+}
+
+/**
+ * Compute "round half to even" division `dividend / divisor`. The divisor must
+ * be a positive number.
+ */
+inline Int128 HalfEvenDiv(const Int128& dividend, const Int128& divisor) {
+ MOZ_ASSERT(divisor > Int128{0}, "negative divisor not supported");
+
+ auto [quotient, remainder] = dividend.divrem(divisor);
+
+ // "Round half to even" division rounds both positive and negative quotients
+ // to the nearest even integer. See [1].
+ //
+ // Int128 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]
+ // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes
+ if ((quotient & Int128{1}) == Int128{1} &&
+ Uint128(remainder.abs()) * Uint128{2} == static_cast<Uint128>(divisor)) {
+ quotient += (dividend > Int128{0}) ? Int128{1} : Int128{-1};
+ }
+ if (Uint128(remainder.abs()) * Uint128{2} > static_cast<Uint128>(divisor)) {
+ quotient += (dividend > Int128{0}) ? Int128{1} : Int128{-1};
+ }
+ return quotient;
+}
+
+/**
+ * Perform `dividend / divisor` and round the result according to the given
+ * rounding mode.
+ */
+inline Int128 Divide(const Int128& dividend, const Int128& 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 */