Skip to content

Read a BigInteger's bytes as bytes, so converting one to a Number stops throwing (#585) - #944

Merged
Rafael-SOWNet merged 2 commits into
masterfrom
fix/bigint-to-number
Aug 14, 2026
Merged

Read a BigInteger's bytes as bytes, so converting one to a Number stops throwing (#585)#944
Rafael-SOWNet merged 2 commits into
masterfrom
fix/bigint-to-number

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Collaborator

Entity and Entity.Number both offer an implicit conversion from System.Numerics.BigInteger, and they did not agree.

Entity        fine  = new BigInteger(123456789);   // 123456789
Entity.Number threw = new BigInteger(123456789);   // FormatException: Illegal character found

Entity's reads the two's-complement bytes. Number's handed the same bytes to EInteger.FromString, whose byte[] overload reads them as ASCII digits:

// was
=> Integer.Create(EInteger.FromString(bigInt.ToByteArray()));
// is
=> Integer.Create(EInteger.FromBytes(bigInt.ToByteArray(), littleEndian: true));

So it failed for every value whose bytes are not digit characters — nearly all of them, 1 included. The handful that worked did so by accident: 12594 is the two bytes '2' and '1', and came back as 21.

Why nothing caught it

Nothing inside the library reaches that conversion. It exists only for callers, so no internal path and no test ever crossed it — the failure mode where a public API is broken precisely because it is public-only.

The test therefore requires the two conversions to agree, not merely that each works. One value converted two ways giving two answers is the defect; the exception is only how it showed up.

58 cases, over values on both sides of every byte boundary that matters — 127, 128, 255, 256, the long limits either side, and a value past 2^127 — plus the accidental-success case pinned so its old behaviour is on record.

How it was found

While documenting the members a #pragma warning disable CS1591 was covering, for #585. The two conversions live in different files and neither looks wrong on its own; reading them side by side is what showed it, and reading them side by side is exactly what documenting them required.

That is the second thing that sweep has turned up after the pragmas that were suppressing nothing.

Measured

Suite 7175 passed / 0 failed, 58 new. Recorded in BREAKING-CHANGES.md under a new Unreleased — since 2.2.0 section: an exception becoming a value is a changed answer.

🤖 Generated with Claude Code

Rafael-SOWNet and others added 2 commits August 14, 2026 21:50
…ps throwing (#585)

Entity and Entity.Number both offer an implicit conversion from System.Numerics.BigInteger, and they
did not agree. Entity's reads the two's-complement bytes. Number's handed the same bytes to
EInteger.FromString, whose byte[] overload reads them as ASCII digits:

    Entity        fine  = new BigInteger(123456789);   // 123456789
    Entity.Number threw = new BigInteger(123456789);   // FormatException: Illegal character found

So it failed for every value whose bytes are not digit characters, which is nearly all of them and
includes 1. The handful that worked worked by accident: 12594 is the two bytes '2' and '1', and came
back as 21.

Nothing inside the library reaches that conversion, which is why nothing caught it -- it exists only
for callers. Both now read the bytes, and the test requires the two conversions to agree rather than
each to work on its own, since one value converted two ways giving two answers is the defect and not
merely the exception.

Found while documenting the members a CS1591 pragma was covering. The two conversions are in
different files and neither looks wrong alone; reading them side by side is what showed it, and
reading them side by side is what documenting them required.

Measured: suite 7175 passed / 0 failed, 58 of them new, over values on both sides of every byte
boundary that matters -- 127, 128, 255, 256, the long limits, and a value past 2^127.

#585

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…utside the parser (#585)

The block this fix edits was the one region still suppressing the missing-documentation warning
once the rest were dealt with, and it is documented here rather than on the documentation branch
because two branches editing one block conflict for whichever merges second.

Each conversion lands on the narrowest kind that holds the value exactly, and the float and double
ones read the binary value that was stored rather than the decimal that was written. The BigInteger
one now says in its remarks what it used to do and why that threw.

With this and the documentation branch, no CS1591 pragma remains in the library outside the
generated parser, which keeps its own because it is regenerated from the grammar.

Measured: suite 7175 passed / 0 failed.

#585

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@Rafael-SOWNet
Rafael-SOWNet merged commit 94859ac into master Aug 14, 2026
25 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.

1 participant