Skip to content

Fix formula format specifier for #.### and 0.00E00 patterns - #81

Merged
yannrichet merged 2 commits into
mainfrom
yannrichet-asnr/formula-decimal-format-patterns
Aug 3, 2026
Merged

Fix formula format specifier for #.### and 0.00E00 patterns#81
yannrichet merged 2 commits into
mainfrom
yannrichet-asnr/formula-decimal-format-patterns

Conversation

@yannrichet-asnr

Copy link
Copy Markdown
Member

Context

Java Funz's formula format specifier (@{expr | pattern}, see Formula.java) delegates directly to java.text.DecimalFormat. FZ already supported the basic fixed-decimal case (@{expr | 0.000}), but two other DecimalFormat pattern styles were silently broken:

  • #.### (significant-digit / trailing-zero-stripping patterns) was treated like 0.000 (fixed decimals, no stripping): @{3.1 | #.###} produced 3.100 instead of 3.1.
  • 0.00E00 (scientific notation) was completely mishandled: the code counted all characters after the first . as decimal places, ignoring the E exponent entirely, producing nonsensical output like 123456.78900 instead of 1.23E05.

Changes

  • fz/interpreter.py: added _format_decimal_pattern() and _format_number() helpers implementing the relevant DecimalFormat subset:
    • 0 → always-shown, zero-padded digit
    • # → digit shown only if significant (trailing zeros stripped)
    • E → scientific notation, with exponent digit count taken from the pattern
    • Used in both the Python and R code paths of evaluate_formulas() (string substitution into template content) and evaluate_single_formula() (typed return value).
  • tests/test_java_funz_compatibility.py: added test_formula_with_hash_pattern_strips_trailing_zeros and test_formula_with_scientific_format.
  • doc/formulas-and-interpreters.md / doc/INDEX.md: documented the formula number-formatting feature (previously implemented and tested but undocumented for users).
  • NEWS.md: release note.

Verification

python3 -m pytest tests/test_java_funz_compatibility.py tests/test_fzi_formulas.py tests/test_interpreter_python.py tests/test_skill_static.py -q
# 51 passed
>>> evaluate_formulas("x = @{3.14159 | 0.000}", {}, {})
'x = 3.142'
>>> evaluate_formulas("x = @{3.1 | #.###}", {}, {})
'x = 3.1'
>>> evaluate_formulas("x = @{123456.789 | 0.00E00}", {}, {})
'x = 1.23E05'
>>> evaluate_formulas("x = @{0.000123456 | 0.00E00}", {}, {})
'x = 1.23E-04'

…#.###, 0.00E00)

The @{expr | pattern} formula format specifier already worked for simple
fixed-decimal patterns like 0.000/0.0000 (matching Java Funz's Formula.java
which delegates to java.text.DecimalFormat), but patterns using '#' digits
or scientific notation ('E') were silently mishandled: '#.###' behaved like
a fixed-decimal pattern (no trailing-zero stripping) and '0.00E00' produced
garbage (decimal count taken from all characters after the first '.',
ignoring the E-exponent entirely).

Add _format_decimal_pattern()/_format_number() helpers implementing the
relevant DecimalFormat subset (0 = zero-padded digit, # = significant-only
digit, E = scientific notation with configurable exponent digits) and use
them in both the Python and R code paths of evaluate_formulas() and
evaluate_single_formula().

Also documents the formula number-formatting feature in
doc/formulas-and-interpreters.md and doc/INDEX.md, which previously had no
user-facing documentation despite being implemented and tested.
Copilot AI review requested due to automatic review settings August 3, 2026 13:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes and extends Java Funz–compatible formula number formatting (@{expr | pattern}) in fz/interpreter.py, aligning FZ’s behavior with the relevant java.text.DecimalFormat pattern subset (notably #.### and 0.00E00), and documents the feature.

Changes:

  • Add DecimalFormat-like formatting helpers (_format_decimal_pattern(), _format_number()) and use them in both evaluate_formulas() (template substitution) and evaluate_single_formula() (typed return path).
  • Add compatibility tests for #.### (strip trailing zeros) and 0.00E00 (scientific notation).
  • Document the formatting feature and add an Unreleased NEWS entry.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
fz/interpreter.py Implements DecimalFormat-like formatting and wires it into Python/R formula evaluation paths.
tests/test_java_funz_compatibility.py Adds regression tests for #.### and 0.00E00 formatting patterns.
doc/formulas-and-interpreters.md Documents number-format patterns for formulas with examples.
doc/INDEX.md Adds an index entry pointing to the new documentation section.
NEWS.md Adds release notes describing the expanded formatting support.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread fz/interpreter.py
Comment on lines +69 to +92
match = re.match(r'^(?P<mantissa>[0#]*(?:\.[0#]*)?)[Ee](?P<exp>[0#]+)$', format_spec)
if match:
mantissa_pattern = match.group('mantissa') or '0'
exp_digits = len(match.group('exp'))
frac_pattern = mantissa_pattern.split('.', 1)[1] if '.' in mantissa_pattern else ''
mantissa_decimals = len(frac_pattern)

if value == 0:
mantissa, exponent = 0.0, 0
else:
exponent = int(math.floor(math.log10(abs(value))))
mantissa = value / (10 ** exponent)
mantissa = round(mantissa, mantissa_decimals)
if abs(mantissa) >= 10:
mantissa /= 10
exponent += 1
elif abs(mantissa) < 1:
mantissa *= 10
exponent -= 1

mantissa_str = _format_decimal_pattern(mantissa, mantissa_pattern)
sign = '-' if exponent < 0 else ''
return f"{mantissa_str}E{sign}{abs(exponent):0{exp_digits}d}"

Comment on lines +128 to +134
content = (
"A: @{123456.789 | 0.00E00}\n"
"B: @{0.000123456 | 0.00E00}"
)
result = evaluate_formulas(content, model, {}, interpreter="python")
assert "A: 1.23E05" in result
assert "B: 1.23E-04" in result
Comment on lines +114 to +118
content = "A: @{3.14159 | #.###}\nB: @{3.1 | #.###}\nC: @{3.0 | #.###}"
result = evaluate_formulas(content, model, {}, interpreter="python")
assert "A: 3.142" in result
assert "B: 3.1" in result
assert "C: 3" in result
@yannrichet
yannrichet merged commit ec564c8 into main Aug 3, 2026
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants