Number Formatting: Research Summary and Request for Feedback

Number formatting is a frequently requested feature, and it is important for typesetting documents that require consistent numeric precision, localized formats, or scientific notation.

Recently, I noticed that most related discussions on GitHub and Discord are over a year old. Since I have a current need for more flexible number formatting, I decided to explore implementing this feature myself.

This post serves as a collection of resources and ideas; even if I am unable to complete the implementation or if it doesn’t meet expectations, I hope this summary will be useful for anyone interested in improving number formatting in Typst in the future.

Resources

GitHub issues

Discord thread

  • According to this comment, here is the forge thread.

Universe packages

Standards & Conventions

  • NIST guide for scientific writing
  • Decimal separator(including thousand separator and radix character) wikipedia

Existing tools

What do we want

Basically:

  • Format a float (or decimal) to a user-friendly string for display purposes.
  • Automatically round to a specified precision, and optionally pad with trailing zeros if needed.
  • Allow formatting style to be controlled via show and set rules, specifically:
    • Customize the thousands separator and grouping size (e.g., not necessarily grouping by thousands).
    • Customize the decimal separator.
    • Optionally format numbers using SI prefixes (e.g., k for thousands, M for millions).
    • Optionally format numbers using scientific notation.
  • Provide an intuitive and easy-to-use interface.
    (Many existing solutions, such as Python’s format strings, are overly complex and resemble small DSLs. They introduce nontrivial learning and memory costs while not being worth the learning effort.)

My Design

I am introducing a new element (currently called digits, though the name is still open for discussion) to wrap decimal numbers. This element can then be styled and controlled via show and set.

All values that can be converted into a decimal will be automatically wrapped as digits when directly inserted into the document, and will be affected by show and set rules (similar to how #type(x) is currently controlled by raw). Here’s a simple example of what it should be like:

#set text(lang: "en", region: "fr") // by default 1.00005e5 should be "10 000,5"

#(12345.6789) // This should be 12 345,6789

#set digits(grouping-marker: ".")

#(12345.6789) // This should be 12.345,6789

Progress

At present, the functionality for fine-grained control over the mentioned formatting behaviors is largely implemented. However, localization based on language and region is not yet handled, and documentation is still missing.

The source code is available here

Known Issues

  • The fractional part of a number typically does not group digits, but my current implementation does not reflect this behavior.
  • Decimals are not suitable for scientific values(e.g. 1e-50)
  • The default formatting behavior should depend on text.lang and text.region, but defining a clear and appropriate standard is challenging.
  • Using a space as a grouping separator may break proper layout in right-to-left (RTL) writing systems.
  • The current code quality is not very high, particularly in field naming and logic structure, and could benefit from further refinement.

Conclusion

I would love to hear your thoughts, suggestions, and questions—whether about the ideas discussed in this post or the current implementation itself.
Feedback on both the design and the code is very welcome!

5 Likes