Skip to content

feat: Add usePlainNumberFormat=all to read numeric cells at full precision regardless of cell format - #1061

Open
vladislav-ishchenko wants to merge 1 commit into
nightscape:mainfrom
vladislav-ishchenko:plain-number-all-cells
Open

vladislav-ishchenko wants to merge 1 commit into
nightscape:mainfrom
vladislav-ishchenko:plain-number-all-cells

Conversation

@vladislav-ishchenko

@vladislav-ishchenko vladislav-ishchenko commented Aug 19, 2026 •

Copy link
Copy Markdown

Closes #1053.

What

usePlainNumberFormat becomes a three-state option parsed to an enum — false (default), true (unchanged: General/@-formatted cells) and the new all. With all, every non-date numeric cell read into a string column — including cached numeric formula results — is rendered through the existing PlainNumberFormat (full precision, no scientific notation), ignoring the cell's number format. Date-formatted cells keep their formatted rendering, text cells stay verbatim, non-finite values keep POI's display rendering. V2 column naming honors all too, so a numeric header cell is named consistently with its own data cells, and all selects the General number style on the write path like true does. The Scala API keeps accepting usePlainNumberFormat = true / = false through an implicit conversion from Boolean; all is PlainNumberFormatMode.All there. Invalid values are rejected with the three allowed ones.

usePlainNumberFormat only registers PlainNumberFormat for the General/@ format strings, so cells with an explicit number format still render their rounded/scientific display value: 84.789 under 0.00 reads as "84.79", and large formatted numbers read as scientific notation (#126, #771). Widening the registration isn't viable — the custom-format map is keyed by exact format string and shared with date rendering, and 2+-part ; conditional formats bypass the map entirely — so the format-independent path branches on the cell instead.

Also in this PR

PlainNumberFormat appended the unstripped BigDecimal, so single-significant-digit values below 1e-3 gained a spurious trailing zero from Double.toString's d.0E-x mantissa: 0.0005 read as "0.00050". It now appends the stripped value. Only that value class is affected (a sweep over 400k random doubles found no other differences), which means usePlainNumberFormat=true on a General-format cell holding 0.0005 now reads "0.0005" instead of "0.00050".

Tests

Both engines (V1 + V2), including the maxRowsInMemory streaming path: explicit-format rounding vs. plain rendering, General-format scientific notation, text cells verbatim, date cells, a cached numeric formula result, the 0.0005 trailing-zero case, numeric header naming, a usePlainNumberFormat=true read pinning the boundary between true and all, and a parse suite for the three values and the rejection of anything else. The V1 tests go through the spark.read.excel(...) DSL so the option-key plumbing is exercised as well.

@vladislav-ishchenko
vladislav-ishchenko force-pushed the plain-number-all-cells branch 2 times, most recently from e591c61 to fd04bc9 Compare October 1, 2026 09:33
@vladislav-ishchenko

Copy link
Copy Markdown
Author

@nightscape glad to see #1040 and #1032 land — thanks.

This one is ready for a look whenever you have a moment: rebased onto current main (on top of
#1032), one commit, 12 files. The CI workflow needs your approval to run here since it's my first
PR in the repo; the identical matrix runs green on my fork:
https://github.com/vladislav-ishchenko/spark-excel/actions?query=branch%3Aplain-number-all-cells

Short version of the why: usePlainNumberFormat only reaches cells whose format is General/@,
so a cell with an explicit format (0.00, #,##0.00) still reads as its rounded display, and the
map it registers into is shared with date rendering, so widening it isn't safe. The new option
branches on the cell instead and renders every non-date numeric cell at full precision. It's
opt-in, default false, no behaviour change unless set.

One thing I'd happily split out if you prefer a narrower diff: the PR also fixes PlainNumberFormat
appending the unstripped value (0.0005 rendered as "0.00050"). It touches the existing option's
output for that one value class, so it's a reasonable thing to want as its own commit.

@nightscape

Copy link
Copy Markdown
Owner

Does it make sense to enhance usePlainNumberFormat to be a multi-state option instead of introducing a new boolean that allows introducing invalid combinations?
We could keep the existing true and false values, but introduce a third option all for this case.
The parsed value would need to be an enum then.

@vladislav-ishchenko

Copy link
Copy Markdown
Author

@nightscape
Agreed - with two booleans an explicit usePlainNumberFormat=false is silently overridden by
usePlainNumberFormatForAllCells=true, and the write path (which keys on usePlainNumberFormat
for the number style) can disagree with the read path. One option with three states is the right
shape.

I'll push it to this PR: usePlainNumberFormat parses to an enum — false / true / all,
case-insensitive like today, anything else rejected with the three allowed values.
false is unchanged; true is unchanged except for the PlainNumberFormat trailing-zero fix already in
this PR (0.0005 read as "0.00050");
all renders every non-date numeric cell at full precision regardless of its number format and selects the General number style on the write path like true does.

The extra boolean is gone. The Scala API keeps accepting usePlainNumberFormat = true / = false through an implicit conversion from Boolean, so existing callers compile unchanged; all is PlainNumberFormatMode.All there.

@vladislav-ishchenko vladislav-ishchenko changed the title feat: Add usePlainNumberFormatForAllCells option to read numeric cells at full precision regardless of cell format feat: Add usePlainNumberFormat=all to read numeric cells at full precision regardless of cell format Oct 1, 2026
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.

Option to read numeric cells at full precision regardless of the cell's number format

2 participants