feat(spreadsheet): 분수 형식 · 함수 2차 묶음(30개) · misc 파일 쪼갬
numfmt — 분수 형식(0/0 · # ?/? · # ??/?? · 분모 고정 # ?/8) 엑셀과 같게 구현. 함수 — MROUND·ODD·EVEN·LOG·LOG10·LN·EXP(math) · SUMIFS·COUNTIFS·AVERAGEIF(S)·MAXIFS·MINIFS· IFS·SWITCH·XLOOKUP(찾기·조건부 집계) · TEXTJOIN·TRIM·UPPER·LOWER·SUBSTITUTE·REPLACE·FIND· SEARCH·REPT·ISNUMBER·ISTEXT·ISBLANK·ISERROR·NA·TODAY·DATE·YEAR·MONTH·DAY(misc) 더함. misc.ts 가 700 줄을 넘어 찾기·조건부 집계를 spreadsheet_functions_lookup.ts(새 파일)로 쪼갬. sub1 의 func_help 이름 목록에 더할 것을 spreadsheet_functions.ts 의 FUNC_HELP_BATCH2 에 둠 (그 파일은 안 고침 · 머리 주석에 읽는 길 적음). 경계값 시험 135건 통과. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RrQ65VtGZbae2VKgYVMhmc
This commit is contained in:
@@ -415,4 +415,76 @@ export const MATH_FUNCTIONS: Record<string, SheetFunction> = {
|
||||
return div(values.reduce(add, ZERO), frac(BigInt(values.length)));
|
||||
},
|
||||
},
|
||||
MROUND: {
|
||||
min: 2,
|
||||
max: 2,
|
||||
call: (args) => {
|
||||
const n = num(args[0]());
|
||||
if (isErr(n)) return n;
|
||||
const m = num(args[1]());
|
||||
if (isErr(m)) return m;
|
||||
if (cmp(m, ZERO) === 0) return ZERO;
|
||||
if (cmp(n, ZERO) !== 0 && cmp(n, ZERO) > 0 !== cmp(m, ZERO) > 0) {
|
||||
return mkErr("#NUM!", "수 · 배수의 부호가 다름");
|
||||
}
|
||||
return mul(frac(toInteger(div(n, m), "round")), m);
|
||||
},
|
||||
},
|
||||
ODD: {
|
||||
min: 1,
|
||||
max: 1,
|
||||
call: (args) => {
|
||||
const n = num(args[0]());
|
||||
if (isErr(n)) return n;
|
||||
let v = toInteger(n, "away");
|
||||
if (v === 0n) v = 1n;
|
||||
else if (v % 2n === 0n) v += v < 0n ? -1n : 1n;
|
||||
return frac(v);
|
||||
},
|
||||
},
|
||||
EVEN: {
|
||||
min: 1,
|
||||
max: 1,
|
||||
call: (args) => {
|
||||
const n = num(args[0]());
|
||||
if (isErr(n)) return n;
|
||||
let v = toInteger(n, "away");
|
||||
if (v % 2n !== 0n) v += v < 0n ? -1n : 1n;
|
||||
return frac(v);
|
||||
},
|
||||
},
|
||||
LOG: {
|
||||
min: 1,
|
||||
max: 2,
|
||||
call: (args) => {
|
||||
const n = num(args[0]());
|
||||
if (isErr(n)) return n;
|
||||
if (cmp(n, ZERO) <= 0) return mkErr("#NUM!");
|
||||
const base = args.length > 1 ? num(args[1]()) : frac(10n);
|
||||
if (isErr(base)) return base;
|
||||
if (cmp(base, ZERO) <= 0 || cmp(base, ONE) === 0) return mkErr("#NUM!");
|
||||
return fromDouble(Math.log(fracToDouble(n)) / Math.log(fracToDouble(base)));
|
||||
},
|
||||
},
|
||||
LOG10: {
|
||||
min: 1,
|
||||
max: 1,
|
||||
call: (args) => {
|
||||
const n = num(args[0]());
|
||||
if (isErr(n)) return n;
|
||||
if (cmp(n, ZERO) <= 0) return mkErr("#NUM!");
|
||||
return fromDouble(Math.log10(fracToDouble(n)));
|
||||
},
|
||||
},
|
||||
LN: {
|
||||
min: 1,
|
||||
max: 1,
|
||||
call: (args) => {
|
||||
const n = num(args[0]());
|
||||
if (isErr(n)) return n;
|
||||
if (cmp(n, ZERO) <= 0) return mkErr("#NUM!");
|
||||
return fromDouble(Math.log(fracToDouble(n)));
|
||||
},
|
||||
},
|
||||
EXP: unaryDouble(Math.exp),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user