Indexed double equality misses -0.0: IndexKey(double) gives the two zeros different keys
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 35/100
Research direction
Start with src/BLite.Core/Indexing/IndexKey.cs and inspect the double encoding around lines 94–105. Compare indexed equality and range results for -0.0 and +0.0 with the plain collection and LINQ-to-Objects; include tests for both range operators. Done means indexed behavior matches IEEE 754 equality and ordering, with the existing-index rebuild implications addressed. Pull request #252 is already open.
Written by the indexing model from the issue text.
Description
Found by a differential test that runs ~330 LINQ shapes over the same documents on a typed collection with and without indexes and with LINQ-to-Objects as the reference (PR to follow). Two rows disagree, on the indexed collection only.
Repro
// DiffDoc has double Ratio with .HasIndex(x => x.Ratio).
await ctx.Docs.InsertBulkAsync([
new DiffDoc { Name = "d1", Ratio = -0.0 },
new DiffDoc { Name = "d2", Ratio = 0.0 },
new DiffDoc { Name = "d3", Ratio = 1.0 }]);
ctx.Docs.AsQueryable().Where(x => x.Ratio == 0.0).ToList(); // expected [d1, d2], actual [d2]
ctx.Docs.AsQueryable().Count(x => x.Ratio == 0.0); // expected 2, actual 1
Expected: IEEE 754 equality, which C#, LINQ-to-Objects and the BSON predicate on the plain collection all use, says -0.0 == 0.0.
Actual: only the +0.0 document when the property is indexed.
Mechanism (verified on 0450b97)
IndexKey(double) is an order-preserving bit encoding (src/BLite.Core/Indexing/IndexKey.cs:94-105): negative values flip all bits, positive values flip the sign bit. -0.0 has the sign bit set and encodes to 0x7FFF…FFFF; +0.0 encodes to 0x8000…0000. Adjacent but distinct keys, and the equality plan is an exact point lookup on the key of +0.0. By the same encoding x.Ratio >= 0.0 excludes -0.0 and x.Ratio < 0.0 includes it, both unlike C# (not covered by a test row yet).
Affected versions: since the sort-preserving double encoding in f83e0d5 (2026-03-17, v3.7.0). Reproduced on v5.2.3 / 0450b97.
Fix
Normalise -0.0 to +0.0 in IndexKey(double) (if (value == 0) value = 0.0; before taking the bits), which fixes stored keys and query bounds together. Existing indexes holding a -0.0 key would need a rebuild to be found by the new bound. NaN is a separate question (C# says NaN != NaN; the encoding gives it one or several keys) and is outside this issue.
Also noticed, not verified: DynamicCollection.BsonValueToIndexKey encodes doubles with BitConverter.GetBytes (DynamicCollection.cs:2725), little-endian and not order-preserving, unlike the typed path, so range bounds on a dynamic double index may be off. The differential test covers typed collections only.
Happy to send the fix if you want it.
- Dominant language
- C#
- Stars
- 66
- Forks
- 6
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 43
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from EntglDb/BLite
-
A DocumentDbContext whose class name does not end in "Context" gets no generated code and no diagnostic; its collections stay nullPossibly taken A pull request linked to this issue is open or already merged. Openbug package/generator triage
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
GdprMode.Strict warns that deleted documents are not zeroed, but every delete zero-fills themPossibly taken A pull request linked to this issue is open or already merged. Openbug gdpr package/core triage
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
Maintainers usually reply within 1 day
-
QueryIndexAsync rejects a DateTime boundPossibly taken @CaffeinatedCoder claimed this 1 day ago. Openarea/indexing bug package/core triage
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
EntglDb/BLite#270 · 1 comment ·
Maintainers usually reply within 1 day
-
BLQL compares two Int64 values through doublePossibly taken @CaffeinatedCoder claimed this 1 day ago. Openarea/linq bug package/core triage
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
EntglDb/BLite#268 · 1 comment ·
Maintainers usually reply within 1 day
-
Collection-per-file mode: creating a collection can route another collection's page to the main file (lock-free read of a Dictionary)Possibly taken @CaffeinatedCoder claimed this 1 day ago. Openbug package/core severity/medium triage
Difficulty 4/5 3-5 days Newbie friendliness 58/100
EntglDb/BLite#312 · 1 comment ·
Maintainers usually reply within 1 day
Similar issues
-
bug priority: P2
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
:watch: Not Triaged dotnet-csharp/svc fundamentals/subsvc
Difficulty 1/5 Under an hour Newbie friendliness 88/100
Maintainers usually reply within 1 day
-
Wrong values of Kernel32.FileShare.Delete and Kernel32.FileAttributes.PosixSemanticsPossibly taken @ElectLive claimed this today. Openbug good first issue help wanted
Difficulty 1/5 Under an hour Newbie friendliness 83/100
ForNeVeR/TruePath#241 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
files-community/Files#19047 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day