fix(m01): 값 식 작은 수를 유효숫자 4 자리로 · 200 원 미만 단가도 (PLAN 5장 4바퀴)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1MKKZKpUHTPKb513FneU8
This commit is contained in:
2026-09-24 11:31:18 +09:00
co-authored by Claude Sonnet 5
parent 0a6bac5056
commit 0d81acc7d3
2 changed files with 23 additions and 3 deletions
+12 -3
View File
@@ -33,10 +33,13 @@ _CALC_ERRORS = (mf.FormulaError, ArithmeticError, KeyError, TypeError, ValueErro
def fmt(value) -> str:
"""화면에 보이는 자릿수 그대로 — 소수 넷째 자리까지 · 뒤 0 은 떼고 · 천 단위 쉼표."""
"""보이는 자릿수 — 소수 넷째 자리·유효숫자 4 자리 중 많은 쪽 · 뒤 0 · 천 단위 쉼표."""
if not isinstance(value, Decimal):
return str(value)
cut = value.quantize(PLACES) if value.as_tuple().exponent < -4 else value
places = max(4, 3 - value.adjusted()) if value else 4 # 0.00167 → 0.001667
cut = value
if value.as_tuple().exponent < -places:
cut = value.quantize(Decimal(1).scaleb(-places))
sign, digits = ("-", -cut) if cut < 0 else ("", cut)
whole, _, rest = f"{digits.normalize():f}".partition(".")
if "E" in whole or "e" in whole: # 아주 큰 수 — 지수 꼴을 펴서 씀
@@ -51,6 +54,12 @@ def fmt_money(value) -> str:
return fmt(value)
def fmt_price(value) -> str:
"""단가 — 정수로 보이되 200 원 미만은 반올림 오차가 커서 유효숫자로."""
small = isinstance(value, Decimal) and abs(value) < 200
return fmt(value) if small else fmt_money(value)
# ── 식을 글로 — 값 꼴과 이름 꼴 ────────────────────────────────────────
def render(node, env: dict, master, depth: int = 0) -> str:
"""식 한 마디 → 값 꼴 글. 값이 정해지는 마디(요소 · 찾기 · 로직 · 이름)는 그 수로 바뀜."""
@@ -377,7 +386,7 @@ def _line_text(item: dict, line: dict, env: dict, master, part: str | None) -> s
qty = _times(render(mf.parse(item["수량"]), env, master), item, env, master)
money = line["비목"][part] if part else line["금액"]
price = line["단가"] if part is None or line["수량"] == 0 else money / line["수량"]
return f"{fmt_money(price)} * {qty} = {fmt_money(money)}"
return f"{fmt_price(price)} * {qty} = {fmt_money(money)}"
def _line_names(item: dict, line: dict, env: dict, master, part: str | None, name_of: dict) -> str:
+11
View File
@@ -377,3 +377,14 @@ def test_요소조각_로직_부르기는_이름과_인자_글_날식_없음() -
assert "GC000268" not in args[""] and "(" not in args[""].split("덤프트럭")[0]
labor = lines[0]["요소조각"]
assert labor[0]["kind"] == "요소" and labor[0][""] == "배관공(수도)"
def test_값_식_작은_수는_유효숫자로_보인_수끼리_곱해도_금액과_맞음() -> None:
"""GC001064 수량 0.001667 — 넷째 자리로 잘라 456 이 465 로 읽히던 것."""
files = store.cm.load(folder=REAL)
row = store.cm.master(REAL).logic("GC001064")
got = mt.lines(files, "GC001064", dict(store.cm.시험입력(row)))
first = _all_lines(got)[0][""]
assert first == "273,308 * 0.001667 = 456"
assert mt.fmt(Decimal("0.00166667")) == "0.001667" and mt.fmt(Decimal("12.34567")) == "12.3457"
assert mt.fmt_price(Decimal("3.5")) == "3.5" and mt.fmt_price(Decimal("3500.4")) == "3,500"