LibJS: Standardize spec prose for Math.sumPrecise

This commit is contained in:
Timothy Flynn 2025-04-28 19:19:59 -04:00 committed by Tim Flynn
parent 9674210ef8
commit 9c85a16aeb
Notes: github-actions[bot] 2025-04-29 11:34:39 +00:00

View file

@ -1025,25 +1025,15 @@ static TwoSumResult two_sum(double x, double y)
return { hi, lo };
}
// https://tc39.es/proposal-math-sum/#sec-math.sumprecise
ThrowCompletionOr<Value> MathObject::sum_precise_impl(VM& vm, Value iterable)
// 2 Math.sumPrecise ( items ), https://tc39.es/proposal-math-sum/#sec-math.sumprecise
JS_DEFINE_NATIVE_FUNCTION(MathObject::sumPrecise)
{
constexpr double MAX_DOUBLE = 1.79769313486231570815e+308; // std::numeric_limits<double>::max()
constexpr double PENULTIMATE_DOUBLE = 1.79769313486231550856e+308; // std::nextafter(DBL_MAX, 0)
constexpr double MAX_ULP = MAX_DOUBLE - PENULTIMATE_DOUBLE;
constexpr double POW_2_1023 = 8.98846567431158e+307; // 2^1023
static constexpr double MAX_DOUBLE = 1.79769313486231570815e+308; // std::numeric_limits<double>::max()
static constexpr double PENULTIMATE_DOUBLE = 1.79769313486231550856e+308; // std::nextafter(DBL_MAX, 0)
static constexpr double MAX_ULP = MAX_DOUBLE - PENULTIMATE_DOUBLE;
static constexpr double POW_2_1023 = 8.98846567431158e+307; // 2^1023
// 1. Perform ? RequireObjectCoercible(items).
TRY(require_object_coercible(vm, iterable));
// 2. Let iteratorRecord be ? GetIterator(items, sync).
auto using_iterator = TRY(iterable.get_method(vm, vm.well_known_symbol_iterator()));
if (!using_iterator)
return vm.throw_completion<TypeError>(ErrorType::NotIterable, iterable.to_string_without_side_effects());
auto iterator = TRY(get_iterator_from_method(vm, iterable, *using_iterator));
enum State {
enum class State {
MinusZero,
PlusInfinity,
MinusInfinity,
@ -1051,7 +1041,15 @@ ThrowCompletionOr<Value> MathObject::sum_precise_impl(VM& vm, Value iterable)
Finite
};
// 3. Let state be minus-zero.
auto items = vm.argument(0);
// 1. Perform ? RequireObjectCoercible(items).
TRY(require_object_coercible(vm, items));
// 2. Let iteratorRecord be ? GetIterator(items, SYNC).
auto iterator_record = TRY(get_iterator(vm, items, IteratorHint::Sync));
// 3. Let state be MINUS-ZERO.
State state = State::MinusZero;
// 4. Let sum be 0.
@ -1060,56 +1058,66 @@ ThrowCompletionOr<Value> MathObject::sum_precise_impl(VM& vm, Value iterable)
u64 count = 0;
Vector<double> partials;
// 6. Let next be not-started.
// 7. Repeat, while next is not done
// 6. Let next be NOT-STARTED.
// 7. Repeat, while next is not DONE
for (;;) {
// a. Set next to ? IteratorStepValue(iteratorRecord).
auto next = TRY(iterator_step_value(vm, iterator));
auto next = TRY(iterator_step_value(vm, iterator_record));
if (!next.has_value())
break;
// If next is not done, then
// i. Set count to count + 1.
count++;
// ii. If count ≥ 2**53, then
// 1. Let error be ThrowCompletion(a newly created RangeError object).
// 2. Return ? IteratorClose(iteratorRecord, error).
if (count >= (1ULL << 53))
return iterator_close(vm, iterator, vm.throw_completion<RangeError>(ErrorType::ArrayMaxSize));
auto next_value = next.value();
// iii. NOTE: The above case is not expected to be reached in practice and is included only so that implementations may rely on inputs being
// "reasonably sized" without violating this specification.
// b. If next is not DONE, then
// i. Set count to count + 1.
++count;
// ii. If count ≥ 2**53, then
if (count >= (1ULL << 53)) {
// 1. Let error be ThrowCompletion(a newly created RangeError object).
auto error = vm.throw_completion<RangeError>(ErrorType::ArrayMaxSize);
// 2. Return ? IteratorClose(iteratorRecord, error).
return iterator_close(vm, iterator_record, error);
}
// iii. NOTE: The above case is not expected to be reached in practice and is included only so that implementations
// may rely on inputs being "reasonably sized" without violating this specification.
// iv. If next is not a Number, then
auto value = next.value();
if (!value.is_number())
if (!next_value.is_number()) {
// 1. Let error be ThrowCompletion(a newly created TypeError object).
auto error = vm.throw_completion<TypeError>(ErrorType::IsNotA, next_value, "number");
// 2. Return ? IteratorClose(iteratorRecord, error).
return iterator_close(vm, iterator, vm.throw_completion<TypeError>(ErrorType::IsNotA, value.to_string_without_side_effects(), "number"));
return iterator_close(vm, iterator_record, error);
}
// v. Let n be next.
auto n = value.as_double();
auto n = next_value.as_double();
// vi. If state is not not-a-number, then
// vi. If state is not NOT-A-NUMBER, then
if (state != State::NotANumber) {
// 1. If n is NaN, then
if (isnan(n)) {
// a. Set state to not-a-number.
if (next_value.is_nan()) {
// a. Set state to NOT-A-NUMBER.
state = State::NotANumber;
} // 2. Else if n is +∞𝔽, then
else if (Value(n).is_positive_infinity()) {
// a. If state is minus-infinity, set state to not-a-number.
// b. Else, set state to plus-infinity.
state = (state == State::MinusInfinity) ? State::NotANumber : State::PlusInfinity;
} // 3. Else if n is -∞𝔽, then
else if (Value(n).is_negative_infinity()) {
// a. If state is plus-infinity, set state to not-a-number.
// b. Else, set state to minus-infinity.
state = (state == State::PlusInfinity) ? State::NotANumber : State::MinusInfinity;
} // 4. Else if n is not -0𝔽 and state is either minus-zero or finite, then
else if (!Value(n).is_negative_zero() && (state == State::MinusZero || state == State::Finite)) {
// a. Set state to finite.
}
// 2. Else if n is +∞𝔽, then
else if (next_value.is_positive_infinity()) {
// a. If state is MINUS-INFINITY, set state to NOT-A-NUMBER.
// b. Else, set state to PLUS-INFINITY.
state = state == State::MinusInfinity ? State::NotANumber : State::PlusInfinity;
}
// 3. Else if n is -∞𝔽, then
else if (next_value.is_negative_infinity()) {
// a. If state is PLUS-INFINITY, set state to NOT-A-NUMBER.
// b. Else, set state to MINUS-INFINITY.
state = state == State::PlusInfinity ? State::NotANumber : State::MinusInfinity;
}
// 4. Else if n is not -0𝔽 and state is either MINUS-ZERO or FINITE, then
else if (!next_value.is_negative_zero() && (state == State::MinusZero || state == State::Finite)) {
// a. Set state to FINITE.
state = State::Finite;
// b. Set sum to sum + (n).
@ -1159,19 +1167,19 @@ ThrowCompletionOr<Value> MathObject::sum_precise_impl(VM& vm, Value iterable)
}
}
// 8. If state is not-a-number, return NaN.
// 8. If state is NOT-A-NUMBER, return NaN.
if (state == State::NotANumber)
return js_nan();
// 9. If state is plus-infinity, return +∞𝔽.
// 9. If state is PLUS-INFINITY, return +∞𝔽.
if (state == State::PlusInfinity)
return js_infinity();
// 10. If state is minus-infinity, return -∞𝔽.
// 10. If state is MINUS-INFINITY, return -∞𝔽.
if (state == State::MinusInfinity)
return js_negative_infinity();
// 11. If state is minus-zero, return -0𝔽.
// 11. If state is MINUS-ZERO, return -0𝔽.
if (state == State::MinusZero)
return Value(-0.0);
@ -1241,13 +1249,7 @@ ThrowCompletionOr<Value> MathObject::sum_precise_impl(VM& vm, Value iterable)
}
}
return Value(hi);
}
// https://tc39.es/proposal-math-sum/#sec-math.sumprecise
JS_DEFINE_NATIVE_FUNCTION(MathObject::sumPrecise)
{
return sum_precise_impl(vm, vm.argument(0));
return hi;
}
}