mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-14 22:54:09 +00:00
fix(functions): reject negative fractions in GCD and LCM (#2072)
* M0: reject negative fractional GCD and LCM arguments Validate the original input before truncation. Add scalar and range regressions, including signed zero, positive fractions and zero-first LCM validation. * M0: document negative-fraction GCD and LCM regression Record tested edge-case behavior and update the parity revision history. Function counts and readiness estimates remain unchanged. * M0: preserve GCD and LCM regression after math test extraction Move the original negative-fraction regression into the existing extracted math tests. Scalar and range cases, zero-first validation, signed zero and positive truncation coverage are unchanged.
This commit is contained in:
@@ -520,7 +520,7 @@ fn integer_args(a: &[Arg]) -> R<Vec<u64>> {
|
||||
v.iter()
|
||||
.map(|x| {
|
||||
let t = x.trunc();
|
||||
if !(0.0..9.007_199_254_740_992e15).contains(&t) { Err(CellError::Num) } else { Ok(t as u64) }
|
||||
if !(0.0..9.007_199_254_740_992e15).contains(x) { Err(CellError::Num) } else { Ok(t as u64) }
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -229,6 +229,20 @@ fn combinatorics() {
|
||||
is_err(ev("MULTINOMIAL", vec![n(-1.0), n(2.0)]), Num);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gcd_and_lcm_reject_negative_fractions_before_truncation() {
|
||||
for name in ["GCD", "LCM"] {
|
||||
for x in [-0.1, -0.5, -0.999] {
|
||||
is_err(ev2(name, x, 2.0), Num);
|
||||
is_err(ev(name, vec![rf(row(&[2.0, x]))]), Num);
|
||||
// A zero elsewhere must not hide an invalid negative argument.
|
||||
is_err(ev2(name, 0.0, x), Num);
|
||||
}
|
||||
close(ev2(name, -0.0, 2.0), if name == "GCD" { 2.0 } else { 0.0 });
|
||||
close(ev2(name, 12.9, 18.9), if name == "GCD" { 6.0 } else { 36.0 });
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trig() {
|
||||
close(ev1("SIN", PI / 2.0), 1.0);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Function parity: worksheet functions and formulas
|
||||
|
||||
> **Last reviewed:** 2026-10-31 · **Last updated:** 2026-11-03 · **Change:** minor (GROUPBY total depth 2 or more adds subtotals; earlier: Python cells are edited in the formula bar; earlier: the Python errors `#PYTHON!`, `#BUSY!`, `#TIMEOUT!` and `#BLOCKED!`, and a Python in cells row; earlier: YEARFRAC basis 0 February-end rule, MOD quotient limit, PERCENTRANK significance; earlier: edge cases matched to Excel: base conversions, BITLSHIFT/BITRSHIFT, UNICHAR noncharacters, UPPER/LOWER/PROPER casing, FIXED/DOLLAR output, F/T/GAMMA/WEIBULL/BETA distributions, BINOM trial cap, HARMEAN; earlier: ECMA.CEILING listed with USDOLLAR as not on Microsoft's list; earlier: IMSQRT reproduces the stray real part Excel gives for negative reals) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
> **Last reviewed:** 2026-10-31 · **Last updated:** 2026-11-04 · **Change:** minor (GCD and LCM reject negative fractional arguments before truncation; earlier: GROUPBY total depth 2 or more adds subtotals; earlier: Python cells are edited in the formula bar; earlier: the Python errors `#PYTHON!`, `#BUSY!`, `#TIMEOUT!` and `#BLOCKED!`, and a Python in cells row; earlier: YEARFRAC basis 0 February-end rule, MOD quotient limit, PERCENTRANK significance; earlier: edge cases matched to Excel: base conversions, BITLSHIFT/BITRSHIFT, UNICHAR noncharacters, UPPER/LOWER/PROPER casing, FIXED/DOLLAR output, F/T/GAMMA/WEIBULL/BETA distributions, BINOM trial cap, HARMEAN; earlier: ECMA.CEILING listed with USDOLLAR as not on Microsoft's list; earlier: IMSQRT reproduces the stray real part Excel gives for negative reals) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
|
||||
Excel's worksheet functions, category by category, against GridCraft's function library. Part of
|
||||
[target-app-parity.md](target-app-parity.md); gaps feed [gaps.md](gaps.md).
|
||||
@@ -132,10 +132,16 @@ Work to close (16–26 h in total): the in-scope missing functions (10–16 h wi
|
||||
an oracle corpus of Excel-computed results per function (owner runs Excel on generated
|
||||
workbooks; results checked in as data, not Excel files) and fixing what it finds (6–10 h).
|
||||
|
||||
GCD and LCM validate the sign of the original argument before truncating it. Negative
|
||||
fractions such as `-0.5` return `#NUM!`, including inside ranges and beside a zero argument.
|
||||
Positive fractional inputs still truncate, and signed zero remains valid. Covered by
|
||||
`gcd_and_lcm_reject_negative_fractions_before_truncation`; function counts and estimates are unchanged.
|
||||
|
||||
## Revision history
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-04 | minor | GCD and LCM reject negative fractional arguments before truncation; scalar and range regressions in the extracted math tests preserve positive truncation and signed-zero behavior; estimates unchanged |
|
||||
| 2026-11-03 | minor | GROUPBY: a total depth of 2 or more adds a subtotal line for each group of the leading key columns (after the group, or before it for a negative depth) and labels the last line Grand Total; a depth past the key columns, or above 1 with `field_relationship` 1, is `#VALUE!`, as in Excel (#1456 item 1) |
|
||||
| 2026-11-01 | minor | Python in cells row: Python mode in the formula bar |
|
||||
| 2026-11-01 | minor | Python in cells (stored, not run): `#PYTHON!`, `#BUSY!`, `#TIMEOUT!` and `#BLOCKED!` done, with their `ERROR.TYPE` numbers; `PY` still counted as missing |
|
||||
|
||||
Reference in New Issue
Block a user