fix(core): Min/Max, NaN and signed zero follow the math, not the old fold (improvise-bw4p.20)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
committed by
Edward Langley
parent
5645f8d24a
commit
9f035f918e
@@ -314,8 +314,8 @@ because they change nothing observable (below); every other is named by at least
|
||||
`group::tests::an_emptied_group_is_absent_and_keeps_no_entries`,
|
||||
`incremental_matches_eager_on_the_shipped_documents`, `incremental_equals_eager_over_random_edit_sequences`;
|
||||
- a group never counts its numbers: `a_group_read_comes_from_a_maintained_group`,
|
||||
`group::tests::min_and_max_skip_nan_and_are_infinite_over_nothing_else` and eight differentials;
|
||||
- `Max` takes the first key: `group::tests::min_and_max_skip_nan_and_are_infinite_over_nothing_else`,
|
||||
`group::tests::min_and_max_skip_nan_and_are_nan_over_nothing_else` and eight differentials;
|
||||
- `Max` takes the first key: `group::tests::min_and_max_skip_nan_and_are_nan_over_nothing_else`,
|
||||
`an_overwrite_costs_two_updates_per_shape_whatever_the_table_size` and five differentials;
|
||||
- `Count` counts one: `a_collapsed_group_reads_one_group_per_member_it_covers`, `group::tests::*`;
|
||||
- unanimity ignores disagreement: `group::tests::text_is_unanimous_or_mixed_across_groups`,
|
||||
@@ -437,7 +437,7 @@ A multiset that ignores a negative weight keeps the minimum it should have lost.
|
||||
|
||||
After the last retraction the group reads as absent and its map holds no zero-weight entry.
|
||||
|
||||
### group::tests::min_and_max_skip_nan_and_are_infinite_over_nothing_else
|
||||
### group::tests::min_and_max_skip_nan_and_are_nan_over_nothing_else
|
||||
|
||||
NaN is skipped by `Min` and `Max`, a group of only NaN gives `+inf` and `-inf` and still counts.
|
||||
|
||||
@@ -472,7 +472,7 @@ What each fault was and the test that names it:
|
||||
`a_formula_that_keeps_moving_for_sixteen_passes_is_circular`,
|
||||
`dependency_order_and_the_plain_passes_leave_the_same_cache`;
|
||||
- dependencies after dependents: `dependency_order_and_the_plain_passes_leave_the_same_cache` and five more;
|
||||
- NaN is not marked circular: `a_formula_cell_holding_nan_is_circular_and_the_cells_that_read_it_keep_the_nan_they_read`;
|
||||
- NaN is not marked circular: `a_formula_cell_holding_nan_is_nan_and_not_circular`;
|
||||
- fifteen passes / seventeen passes: `a_chain_added_last_link_first_is_circular_exactly_where_eager_says`;
|
||||
- the passes end without marking the cells still moving, or begin from the old cache:
|
||||
`a_chain_added_last_link_first_is_circular_exactly_where_eager_says`,
|
||||
@@ -557,7 +557,7 @@ the whole recompute.
|
||||
`IF(Revenue > 1000, B, Revenue)`: at the stem of a big product it reads the cache of the stem of B,
|
||||
which is evaluated after it. Fails when such a formula set is not wide.
|
||||
|
||||
### a_formula_cell_holding_nan_is_circular_and_the_cells_that_read_it_keep_the_nan_they_read
|
||||
### a_formula_cell_holding_nan_is_nan_and_not_circular
|
||||
|
||||
NaN is not equal to itself, so eager's passes never see it settle. Fails when the ordered path leaves it.
|
||||
|
||||
@@ -643,7 +643,7 @@ runs because its two heaviest tests shared it. The tests are unchanged.
|
||||
East plus the maximum of West), the first version kept 14. Fails when a new aggregation dirties only
|
||||
the stems of formulas that name the measure, and when `Stems::carrying` ignores the measure.
|
||||
|
||||
### a_new_aggregation_reaches_a_product_targeted_filtered_sum
|
||||
### a_new_aggregation_reaches_a_filtered_sum_on_the_fallback_path
|
||||
|
||||
The reviewer's second repro, a Product-targeted `SUM(F0 WHERE Region = East)`. It already agreed on this
|
||||
fixture and stays as the second shape of the same hazard.
|
||||
@@ -766,3 +766,22 @@ average of a rounded sum.
|
||||
|
||||
Infinities and NaN read as `ExactSum::value` does, and a zero keeps its sign.
|
||||
|
||||
## Eager's math, fixed (follow-up to steps 4 to 6)
|
||||
|
||||
Ruling (user, 2026-10-10): do not reproduce math bugs found in Eager; fix Eager. Eight faults were
|
||||
injected (six in the table below, two more in the groups' NaN handling); `nan-never-settles` survived the
|
||||
first pass because NaN produced by one evaluation always has the same bits, and is closed by a unit test of
|
||||
`settled`. Each fix has a regression test named after the bug:
|
||||
- `model::types::eager_math_tests::min_and_max_of_both_zeros_do_not_depend_on_their_order` and
|
||||
`with_both_zeros_min_is_negative_and_max_is_positive_whatever_the_order`: a naive `f64::min` fold
|
||||
passes on ARM and fails on other platforms, so the rival is a fold that keeps the first of two zeros;
|
||||
- `model::types::eager_math_tests::min_and_max_over_only_nan_are_nan_not_infinite`,
|
||||
`min_and_max_over_only_nan_are_nan_in_both_backends`, `group::tests::min_and_max_skip_nan_and_are_nan_over_nothing_else`;
|
||||
- `model::types::eager_math_tests::a_formula_that_settles_on_nan_is_nan_and_not_circular`,
|
||||
`a_formula_cell_holding_nan_is_nan_and_not_circular`, `settled_treats_every_nan_as_one_value_and_the_zeros_as_two`;
|
||||
- `model::types::eager_math_tests::a_zero_that_changes_sign_between_passes_settles_on_the_new_sign` and
|
||||
`ordered_evaluation_keeps_the_zero_eager_settles_on` (now with the sign settled by bits, no fallback);
|
||||
- `model::types::eager_math_tests::a_formula_that_never_settles_is_still_circular`: the guard that a real
|
||||
oscillation is still `circular`.
|
||||
The random and differential suites compare numbers by bits and their pools hold both zeros again.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user