Skip to content

Refuse a comparison against NaN rather than ordering it (#947) - #954

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
fix/real-nan-comparisons
Aug 16, 2026
Merged

Refuse a comparison against NaN rather than ordering it (#947)#954
Rafael-SOWNet merged 1 commit into
masterfrom
fix/real-nan-comparisons

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Collaborator

Closes #947.

Real's >, >=, < and <= ordered NaN above every number:

Real one = 1;
Real.NaN > one     // was: true     now: false
Real.NaN >= one    // was: true     now: false
one < Real.NaN     // was: true     now: false

That is EDecimal's total order showing through, and it is not what double does, where every comparison against NaN is false in both directions.

The failure it caused is one-sided, and it is the unsafe side. A guard written as if (value > threshold) treated an undefined value as exceeding the threshold. Of the two possible defaults that is the worse one, and the library's stated position is that answering wrongly is worse than not answering.

CompareTo is deliberately unchanged

This is the part worth reviewing, because it looks like an inconsistency and is not one.

Sorting requires a total order — Array.Sort may loop or throw without one — while an operator requires no such thing. "Where does this sort" and "is this greater" are different questions, and only the second has no answer for a value that is not a number. So CompareTo still puts NaN above every number, and a caller who was relying on the operators to sort should call it instead.

Both halves are pinned by RealComparisonTest, including a test that actually sorts an array containing NaN, because each half reads as a mistake on its own and only makes sense beside the other.

Evidence

  • suite 7263 passed, 0 failed, 14 skipped — nothing in the library relied on the old ordering
  • the test also pins NaN's comparisons against itself (NaN >= NaN is now false), which is the row most likely to be got wrong from habit
  • recorded in BREAKING-CHANGES.md, marked Silent: the call still succeeds and returns the opposite boolean

What it does not do

  • Does not touch CompareTo, Equals, or ==.
  • Does not touch Entity's comparison operators, which build an inequality node rather than answering a bool — a different thing entirely.
  • Does not change how NaN sorts anywhere.

🤖 Generated with Claude Code

Real's >, >=, < and <= ordered NaN above every number, so NaN > 1 was true and
1 < NaN was true as well -- EDecimal's total order showing through, and not what
double does, where every comparison against NaN is false in both directions.

The failure it caused is one-sided and it is the unsafe side: a guard written as
`if (value > threshold)` treated an undefined value as exceeding the threshold. Not
answering is a legitimate answer here and answering wrongly is not.

CompareTo is deliberately left ordering NaN. Sorting needs a total order and Array.Sort
may loop or throw without one, while an operator needs no such thing; "where does this
sort" and "is this greater" are different questions and only the second has no answer
for a value that is not a number. Both halves are now pinned by the test, because
each reads as an inconsistency on its own and only makes sense beside the other.

Suite 7263 passed, 0 failed -- nothing in the library relied on the old ordering.
Recorded in BREAKING-CHANGES.md as silent.
@Rafael-SOWNet
Rafael-SOWNet merged commit a45a725 into master Aug 16, 2026
25 checks passed
@Rafael-SOWNet
Rafael-SOWNet deleted the fix/real-nan-comparisons branch August 16, 2026 00:30
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.

Real's comparison operators put NaN above every number, unlike double

1 participant