mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-31 06:06:48 -06:00
@@ -91,6 +91,8 @@ fn f_pdf(x: f64, d1: f64, d2: f64) -> f64 {
|
||||
((d1 / 2.0) * (d1 / d2).ln() + (d1 / 2.0 - 1.0) * x.ln() - ((d1 + d2) / 2.0) * (1.0 + d1 * x / d2).ln() - ln_beta(d1 / 2.0, d2 / 2.0)).exp()
|
||||
}
|
||||
|
||||
const MAX_BINOM_TRIALS: f64 = 2_147_483_646.0;
|
||||
|
||||
fn binom_pmf(k: f64, n: f64, p: f64) -> f64 {
|
||||
if k < 0.0 || k > n {
|
||||
return 0.0;
|
||||
@@ -437,7 +439,7 @@ pub(super) fn binom_dist(a: &[Arg], _: C) -> R<Value> {
|
||||
let n = num(a, 1)?.trunc();
|
||||
let p = num(a, 2)?;
|
||||
let cum = boolean(a, 3)?;
|
||||
if k < 0.0 || k > n || !(0.0..=1.0).contains(&p) {
|
||||
if k < 0.0 || n > MAX_BINOM_TRIALS || k > n || !(0.0..=1.0).contains(&p) {
|
||||
return Err(CellError::Num);
|
||||
}
|
||||
num_val(if cum { binom_cdf(k, n, p) } else { binom_pmf(k, n, p) })
|
||||
@@ -448,7 +450,7 @@ pub(super) fn binom_dist_range(a: &[Arg], _: C) -> R<Value> {
|
||||
let p = num(a, 1)?;
|
||||
let s1 = num(a, 2)?.trunc();
|
||||
let s2 = if has(a, 3) { num(a, 3)?.trunc() } else { s1 };
|
||||
if !(0.0..=1e15).contains(&n) || !(0.0..=1.0).contains(&p) || s1 < 0.0 || s1 > n || s2 < s1 || s2 > n {
|
||||
if !(0.0..=MAX_BINOM_TRIALS).contains(&n) || !(0.0..=1.0).contains(&p) || s1 < 0.0 || s1 > n || s2 < s1 || s2 > n {
|
||||
return Err(CellError::Num);
|
||||
}
|
||||
if s2 - s1 > 10_000.0 {
|
||||
|
||||
@@ -471,7 +471,15 @@ fn discrete() {
|
||||
close_tol(ev("BINOM.DIST", vec![n(6.0), n(10.0), n(0.5), b(false)]), 0.205078125, 1e-12);
|
||||
close_tol(ev("BINOM.DIST", vec![n(6.0), n(10.0), n(0.5), b(true)]), 0.828125, 1e-12);
|
||||
close_tol(ev("BINOMDIST", vec![n(6.0), n(10.0), n(0.5), b(false)]), 0.205078125, 1e-12);
|
||||
close(ev("BINOM.DIST", vec![n(1.0), n(2_147_483_646.0), n(0.0), b(false)]), 0.0);
|
||||
close(ev("BINOM.DIST", vec![n(1.0), n(2_147_483_646.9), n(0.0), b(false)]), 0.0);
|
||||
is_err(ev("BINOM.DIST", vec![n(1.0), n(2_147_483_647.0), n(0.0), b(false)]), CellError::Num);
|
||||
is_err(ev("BINOM.DIST", vec![n(1.0), n(2_147_483_647.9), n(0.0), b(false)]), CellError::Num);
|
||||
is_err(ev("BINOM.DIST", vec![n(11.0), n(10.0), n(0.5), b(false)]), CellError::Num);
|
||||
close(ev("BINOM.DIST.RANGE", vec![n(2_147_483_646.0), n(0.0), n(1.0)]), 0.0);
|
||||
close(ev("BINOM.DIST.RANGE", vec![n(2_147_483_646.9), n(0.0), n(1.0)]), 0.0);
|
||||
is_err(ev("BINOM.DIST.RANGE", vec![n(2_147_483_647.0), n(0.0), n(1.0)]), CellError::Num);
|
||||
is_err(ev("BINOM.DIST.RANGE", vec![n(2_147_483_647.9), n(0.0), n(1.0)]), CellError::Num);
|
||||
close_tol(ev("BINOM.DIST.RANGE", vec![n(60.0), n(0.75), n(48.0)]), 0.083974967, 1e-6);
|
||||
close_tol(ev("BINOM.DIST.RANGE", vec![n(60.0), n(0.75), n(45.0), n(50.0)]), 0.523629793, 1e-6);
|
||||
close(ev("BINOM.INV", vec![n(6.0), n(0.5), n(0.75)]), 4.0);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Function parity: worksheet functions and formulas
|
||||
|
||||
> **Last reviewed:** 2026-10-31 · **Last updated:** 2026-10-31 · **Change:** minor (WEIBULL.DIST density is zero at x=0, including shape below one, as in Excel; earlier: GAMMA.DIST density at x=0 and shape 1 returns `#NUM!` as in Excel; earlier: ECMA.CEILING listed with USDOLLAR as not on Microsoft's list; earlier: IMSQRT reproduces the stray real part Excel gives for negative reals, and the other IM* differences from Excel are listed; earlier: the LAMBDA row lists the recursion limit and its `#NUM!`, and the error a defined name holding an error gives when called; earlier: number text with accounting parentheses coerces like Excel 365; INDEX with one index on a 2-D reference is #REF! as in Excel; earlier: SUBTOTAL and AGGREGATE cap the cells they visit, #2175) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
> **Last reviewed:** 2026-10-31 · **Last updated:** 2026-10-31 · **Change:** minor (BINOM.DIST and BINOM.DIST.RANGE refuse trial counts above 2,147,483,646 after truncation; earlier: WEIBULL.DIST density is zero at x=0, including shape below one, as in Excel; earlier: ECMA.CEILING listed with USDOLLAR as not on Microsoft's list; earlier: IMSQRT reproduces the stray real part Excel gives for negative reals, and the other IM* differences from Excel are listed) · **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).
|
||||
@@ -98,6 +98,10 @@ This improves series preparation; it does not change the model-fitting or season
|
||||
|
||||
## Depth (estimated)
|
||||
|
||||
| Function | Excel parity note |
|
||||
|---|---|
|
||||
| BINOM.DIST / BINOM.DIST.RANGE | Trial count is truncated, then must be at most 2,147,483,646; larger trials return `#NUM!`. |
|
||||
|
||||
**~82% ready.** The library is broad and has 96 test functions in `crates/functions` with many
|
||||
assertions each, but there is no conformance suite against Excel-computed results, and user
|
||||
reports keep finding edge cases: MIN() with no arguments (#138), COUNTBLANK past the used range
|
||||
@@ -138,6 +142,7 @@ workbooks; results checked in as data, not Excel files) and fixing what it finds
|
||||
| 2026-10-31 | minor | T.INV probability zero returns `#DIV/0!`, while other out-of-range probabilities and invalid degrees remain `#NUM!`; T.INV.2T keeps its separate boundary behavior |
|
||||
| 2026-10-31 | minor | GAMMA.DIST density at x=0 and shape 1 returns `#NUM!` for all positive scales; the cumulative branch and other shape boundaries are unchanged |
|
||||
| 2026-10-31 | minor | WEIBULL.DIST returns zero at x=0 in both density and cumulative modes, including shape below one; negative x remains `#NUM!` |
|
||||
| 2026-10-31 | minor | BINOM.DIST and BINOM.DIST.RANGE reject trial counts above 2,147,483,646 after truncation |
|
||||
| 2026-10-27 | minor | ECMA.CEILING added to the functions that are in our library but not on Microsoft's list (with USDOLLAR), so the counts don't change |
|
||||
| 2026-10-26 | minor | IMSQRT uses Excel's polar route with its cosine reduction, so `IMSQRT("-4")` is `1.22514845490862E-16+2i` as in Excel (exact `2i` before); the rows list the IM* differences still open |
|
||||
| 2026-10-25 | minor | LAMBDA row: a LAMBDA that calls itself more than 140 times, and a formula nested past the depth limit, give `#NUM!` (#1809; nesting gave `#CALC!` before). `=Foo(1)` where the defined name Foo holds `#N/A` gives `#N/A`, not `#NAME?` |
|
||||
|
||||
Reference in New Issue
Block a user