mirror of
https://github.com/tc39/test262.git
synced 2025-07-03 12:14:37 +02:00
Add tests for precise results in Duration.p.total and ZonedDateTime.p.hoursInDay
The existing tests didn't cover some edge cases where implementations have to compute the exact result of `numerator / denominator`, where at least one of `numerator` and `denominator` can't be exactly represented by an IEEE-754 double precision floating point value. "precision-exact-mathematical-values-5.js" gets added in #3961, so the new tests from this commit start at "precision-exact-mathematical-values-6.js".
This commit is contained in:
parent
99d5bc8c1b
commit
0de91996e7
140
test/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-6.js
vendored
Normal file
140
test/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-6.js
vendored
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Copyright (C) 2024 André Bargull. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.duration.prototype.total
|
||||||
|
description: >
|
||||||
|
DivideNormalizedTimeDuration computes on exact mathematical values.
|
||||||
|
info: |
|
||||||
|
Temporal.Duration.prototype.total ( totalOf )
|
||||||
|
|
||||||
|
...
|
||||||
|
20. Let roundRecord be ? RoundDuration(unbalanceResult.[[Years]],
|
||||||
|
unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], days, norm, 1,
|
||||||
|
unit, "trunc", plainRelativeTo, calendarRec, zonedRelativeTo, timeZoneRec,
|
||||||
|
precalculatedPlainDateTime).
|
||||||
|
21. Return 𝔽(roundRecord.[[Total]]).
|
||||||
|
|
||||||
|
RoundDuration ( ... )
|
||||||
|
|
||||||
|
...
|
||||||
|
14. Else if unit is "hour", then
|
||||||
|
a. Let divisor be 3.6 × 10^12.
|
||||||
|
b. Set total to DivideNormalizedTimeDuration(norm, divisor).
|
||||||
|
...
|
||||||
|
|
||||||
|
DivideNormalizedTimeDuration ( d, divisor )
|
||||||
|
|
||||||
|
1. Assert: divisor ≠ 0.
|
||||||
|
2. Return d.[[TotalNanoseconds]] / divisor.
|
||||||
|
features: [Temporal]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
// Randomly generated test data.
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
hours: 816,
|
||||||
|
nanoseconds: 2049_187_497_660,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 7825,
|
||||||
|
nanoseconds: 1865_665_040_770,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 0,
|
||||||
|
nanoseconds: 1049_560_584_034,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 2055144,
|
||||||
|
nanoseconds: 2502_078_444_371,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 31,
|
||||||
|
nanoseconds: 1010_734_758_745,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 24,
|
||||||
|
nanoseconds: 2958_999_560_387,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 0,
|
||||||
|
nanoseconds: 342_058_521_588,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 17746,
|
||||||
|
nanoseconds: 3009_093_506_309,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 4,
|
||||||
|
nanoseconds: 892_480_914_569,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 3954,
|
||||||
|
nanoseconds: 571_647_777_618,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 27,
|
||||||
|
nanoseconds: 2322_199_502_640,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 258054064,
|
||||||
|
nanoseconds: 2782_411_891_222,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 1485,
|
||||||
|
nanoseconds: 2422_559_903_100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 0,
|
||||||
|
nanoseconds: 1461_068_214_153,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 393,
|
||||||
|
nanoseconds: 1250_229_561_658,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 0,
|
||||||
|
nanoseconds: 91_035_820,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 0,
|
||||||
|
nanoseconds: 790_982_655,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 150,
|
||||||
|
nanoseconds: 608_531_524,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 5469,
|
||||||
|
nanoseconds: 889_204_952,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 7870,
|
||||||
|
nanoseconds: 680_042_770,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const nsPerHour = 3600_000_000_000;
|
||||||
|
|
||||||
|
const fractionDigits = Math.log10(nsPerHour) + Math.log10(100_000_000_000) - Math.log10(36);
|
||||||
|
assert.sameValue(fractionDigits, 22);
|
||||||
|
|
||||||
|
for (let {hours, nanoseconds} of data) {
|
||||||
|
assert(nanoseconds < nsPerHour);
|
||||||
|
|
||||||
|
// Compute enough fractional digits to approximate the exact result. Use BigInts
|
||||||
|
// to avoid floating point precision loss. Fill to the left with implicit zeros.
|
||||||
|
let fraction = ((BigInt(nanoseconds) * 100_000_000_000n) / 36n).toString().padStart(fractionDigits, "0");
|
||||||
|
|
||||||
|
// Get the Number approximation from the string representation.
|
||||||
|
let expected = Number(`${hours}.${fraction}`);
|
||||||
|
|
||||||
|
let d = Temporal.Duration.from({hours, nanoseconds});
|
||||||
|
let actual = d.total("hours");
|
||||||
|
|
||||||
|
assert.sameValue(
|
||||||
|
actual,
|
||||||
|
expected,
|
||||||
|
`hours=${hours}, nanoseconds=${nanoseconds}`,
|
||||||
|
);
|
||||||
|
}
|
119
test/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-7.js
vendored
Normal file
119
test/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-7.js
vendored
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
// Copyright (C) 2024 André Bargull. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.duration.prototype.total
|
||||||
|
description: >
|
||||||
|
DivideNormalizedTimeDuration computes on exact mathematical values.
|
||||||
|
info: |
|
||||||
|
Temporal.Duration.prototype.total ( totalOf )
|
||||||
|
|
||||||
|
...
|
||||||
|
20. Let roundRecord be ? RoundDuration(unbalanceResult.[[Years]],
|
||||||
|
unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], days, norm, 1,
|
||||||
|
unit, "trunc", plainRelativeTo, calendarRec, zonedRelativeTo, timeZoneRec,
|
||||||
|
precalculatedPlainDateTime).
|
||||||
|
21. Return 𝔽(roundRecord.[[Total]]).
|
||||||
|
|
||||||
|
RoundDuration ( ... )
|
||||||
|
|
||||||
|
...
|
||||||
|
16. Else if unit is "second", then
|
||||||
|
a. Let divisor be 10^9.
|
||||||
|
b. Set total to DivideNormalizedTimeDuration(norm, divisor).
|
||||||
|
...
|
||||||
|
17. Else if unit is "millisecond", then
|
||||||
|
a. Let divisor be 10^6.
|
||||||
|
b. Set total to DivideNormalizedTimeDuration(norm, divisor).
|
||||||
|
...
|
||||||
|
18. Else if unit is "microsecond", then
|
||||||
|
a. Let divisor be 10^3.
|
||||||
|
b. Set total to DivideNormalizedTimeDuration(norm, divisor).
|
||||||
|
...
|
||||||
|
|
||||||
|
DivideNormalizedTimeDuration ( d, divisor )
|
||||||
|
|
||||||
|
1. Assert: divisor ≠ 0.
|
||||||
|
2. Return d.[[TotalNanoseconds]] / divisor.
|
||||||
|
features: [Temporal]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
// Test duration units where the fractional part is a power of ten.
|
||||||
|
const units = [
|
||||||
|
"seconds", "milliseconds", "microseconds", "nanoseconds",
|
||||||
|
];
|
||||||
|
|
||||||
|
// Conversion factors to nanoseconds precision.
|
||||||
|
const toNanos = {
|
||||||
|
"seconds": 1_000_000_000n,
|
||||||
|
"milliseconds": 1_000_000n,
|
||||||
|
"microseconds": 1_000n,
|
||||||
|
"nanoseconds": 1n,
|
||||||
|
};
|
||||||
|
|
||||||
|
const integers = [
|
||||||
|
// Small integers.
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
|
||||||
|
// Large integers around Number.MAX_SAFE_INTEGER.
|
||||||
|
2**51,
|
||||||
|
2**52,
|
||||||
|
2**53,
|
||||||
|
2**54,
|
||||||
|
];
|
||||||
|
|
||||||
|
const fractions = [
|
||||||
|
// True fractions.
|
||||||
|
0, 1, 10, 100, 125, 200, 250, 500, 750, 800, 900, 950, 999,
|
||||||
|
|
||||||
|
// Fractions with overflow.
|
||||||
|
1_000,
|
||||||
|
1_999,
|
||||||
|
2_000,
|
||||||
|
2_999,
|
||||||
|
3_000,
|
||||||
|
3_999,
|
||||||
|
4_000,
|
||||||
|
4_999,
|
||||||
|
|
||||||
|
999_999,
|
||||||
|
1_000_000,
|
||||||
|
1_000_001,
|
||||||
|
|
||||||
|
999_999_999,
|
||||||
|
1_000_000_000,
|
||||||
|
1_000_000_001,
|
||||||
|
];
|
||||||
|
|
||||||
|
const maxTimeDuration = (2n ** 53n) * (10n ** 9n) - 1n;
|
||||||
|
|
||||||
|
// Iterate over all units except the last one.
|
||||||
|
for (let unit of units.slice(0, -1)) {
|
||||||
|
let smallerUnit = units[units.indexOf(unit) + 1];
|
||||||
|
|
||||||
|
for (let integer of integers) {
|
||||||
|
for (let fraction of fractions) {
|
||||||
|
// Total nanoseconds must not exceed |maxTimeDuration|.
|
||||||
|
let totalNanoseconds = BigInt(integer) * toNanos[unit] + BigInt(fraction) * toNanos[smallerUnit];
|
||||||
|
if (totalNanoseconds > maxTimeDuration) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the Number approximation from the string representation.
|
||||||
|
let i = BigInt(integer) + BigInt(fraction) / 1000n;
|
||||||
|
let f = String(fraction % 1000).padStart(3, "0");
|
||||||
|
let expected = Number(`${i}.${f}`);
|
||||||
|
|
||||||
|
let d = Temporal.Duration.from({[unit]: integer, [smallerUnit]: fraction});
|
||||||
|
let actual = d.total(unit);
|
||||||
|
|
||||||
|
assert.sameValue(
|
||||||
|
actual,
|
||||||
|
expected,
|
||||||
|
`${unit}=${integer}, ${smallerUnit}=${fraction}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
139
test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values-2.js
vendored
Normal file
139
test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values-2.js
vendored
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// Copyright (C) 2024 André Bargull. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-get-temporal.zoneddatetime.prototype.hoursinday
|
||||||
|
description: >
|
||||||
|
Hours in day is correctly rounded using precise mathematical values.
|
||||||
|
info: |
|
||||||
|
get Temporal.ZonedDateTime.prototype.hoursInDay
|
||||||
|
|
||||||
|
...
|
||||||
|
14. Let diffNs be tomorrowInstant.[[Nanoseconds]] - todayInstant.[[Nanoseconds]].
|
||||||
|
15. Return 𝔽(diffNs / (3.6 × 10^12)).
|
||||||
|
features: [Temporal]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
// Randomly generated test data.
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
hours: 816,
|
||||||
|
nanoseconds: 2049_187_497_660,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 7825,
|
||||||
|
nanoseconds: 1865_665_040_770,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 0,
|
||||||
|
nanoseconds: 1049_560_584_034,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 2055144,
|
||||||
|
nanoseconds: 2502_078_444_371,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 31,
|
||||||
|
nanoseconds: 1010_734_758_745,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 24,
|
||||||
|
nanoseconds: 2958_999_560_387,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 0,
|
||||||
|
nanoseconds: 342_058_521_588,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 17746,
|
||||||
|
nanoseconds: 3009_093_506_309,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 4,
|
||||||
|
nanoseconds: 892_480_914_569,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 3954,
|
||||||
|
nanoseconds: 571_647_777_618,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 27,
|
||||||
|
nanoseconds: 2322_199_502_640,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 258054064,
|
||||||
|
nanoseconds: 2782_411_891_222,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 1485,
|
||||||
|
nanoseconds: 2422_559_903_100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 0,
|
||||||
|
nanoseconds: 1461_068_214_153,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 393,
|
||||||
|
nanoseconds: 1250_229_561_658,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 0,
|
||||||
|
nanoseconds: 91_035_820,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 0,
|
||||||
|
nanoseconds: 790_982_655,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 150,
|
||||||
|
nanoseconds: 608_531_524,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 5469,
|
||||||
|
nanoseconds: 889_204_952,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hours: 7870,
|
||||||
|
nanoseconds: 680_042_770,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const nsPerHour = 3600_000_000_000;
|
||||||
|
|
||||||
|
const fractionDigits = Math.log10(nsPerHour) + Math.log10(100_000_000_000) - Math.log10(36);
|
||||||
|
assert.sameValue(fractionDigits, 22);
|
||||||
|
|
||||||
|
for (let {hours, nanoseconds} of data) {
|
||||||
|
assert(nanoseconds < nsPerHour);
|
||||||
|
|
||||||
|
// Compute enough fractional digits to approximate the exact result. Use BigInts
|
||||||
|
// to avoid floating point precision loss. Fill to the left with implicit zeros.
|
||||||
|
let fraction = ((BigInt(nanoseconds) * 100_000_000_000n) / 36n).toString().padStart(fractionDigits, "0");
|
||||||
|
|
||||||
|
// Get the Number approximation from the string representation.
|
||||||
|
let expected = Number(`${hours}.${fraction}`);
|
||||||
|
|
||||||
|
let todayInstant = 0n;
|
||||||
|
let tomorrowInstant = BigInt(hours) * BigInt(nsPerHour) + BigInt(nanoseconds);
|
||||||
|
|
||||||
|
let timeZone = new class extends Temporal.TimeZone {
|
||||||
|
#getPossibleInstantsFor = 0;
|
||||||
|
|
||||||
|
getPossibleInstantsFor() {
|
||||||
|
if (++this.#getPossibleInstantsFor === 1) {
|
||||||
|
return [new Temporal.Instant(todayInstant)];
|
||||||
|
}
|
||||||
|
assert.sameValue(this.#getPossibleInstantsFor, 2);
|
||||||
|
return [new Temporal.Instant(tomorrowInstant)];
|
||||||
|
}
|
||||||
|
}("UTC");
|
||||||
|
|
||||||
|
let zdt = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
let actual = zdt.hoursInDay;
|
||||||
|
|
||||||
|
assert.sameValue(
|
||||||
|
actual,
|
||||||
|
expected,
|
||||||
|
`hours=${hours}, nanoseconds=${nanoseconds}`,
|
||||||
|
);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user