Conversation
…, 0.1) The shared comparator JsonElementHelpers.CompareNormalizedJsonNumbers orders two normalized numbers by their effective length (significand length + exponent), the position of the most-significant digit. Zero normalizes to an empty significand with exponent 0, giving it effective length 0 -- the same band as values in [0.1, 1). Any value whose magnitude is < 0.1 (effective length < 0, e.g. 0.05, 0.083) was therefore ordered as smaller than zero, so minimum/exclusiveMinimum: 0 wrongly rejected such values and maximum/exclusiveMaximum: 0 wrongly accepted them. Values of 0, values >= 0.1, negatives (handled by the sign step), non-zero bounds, and integer schemas were all unaffected. Detect the empty-significand (zero) case explicitly and order it before the effective-length comparison: zero is less than every positive value and greater than every negative value. One-site runtime fix in the shared comparator used by generated models, the standalone evaluator, the dynamic Validator, and JsonLogic; the only generation-time caller (IsUInt64) guards exponent != 0 and compares integers >= 1, so generated output is unchanged and no regeneration is required. Tests (each fails before the fix, passes after): - NumericCoreOperationsTests.CompareNormalizedJsonNumbers: 12 comparator-level rows covering zero vs sub-0.1 magnitudes both directions, plus sign-path and >= 0.1 regression guards. - New ZeroNumericBoundTests: end-to-end dynamic-Validator coverage of minimum/exclusiveMinimum/maximum/exclusiveMaximum = 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Code Coverage Summary Report - Linux (net10.0)Summary
CoverageCorvus.Json.CodeGeneration - 80.3%
Corvus.Json.CodeGeneration.CSharp - 81.7%
Corvus.Json.ExtendedTypes - 74.9%
Corvus.Json.JsonReference - 76.6%
Corvus.Text.Json - 95%
Corvus.Text.Json.AsyncApi - 64.4%
Corvus.Text.Json.AsyncApi.Amqp - 0.6%
Corvus.Text.Json.AsyncApi.AzureServiceBus - 0%
Corvus.Text.Json.AsyncApi.CodeGeneration - 93.2%
Corvus.Text.Json.AsyncApi.Kafka - 0.6%
Corvus.Text.Json.AsyncApi.Mqtt - 0.9%
Corvus.Text.Json.AsyncApi.Nats - 0.4%
Corvus.Text.Json.AsyncApi.Polly - 100%
Corvus.Text.Json.AsyncApi.Testing - 96.6%
Corvus.Text.Json.AsyncApi.WebSocket - 0%
Corvus.Text.Json.CodeGeneration - 93.1%
Corvus.Text.Json.JMESPath - 93.8%
Corvus.Text.Json.JMESPath.CodeGeneration - 97.7%
Corvus.Text.Json.Jsonata - 91.6%
Corvus.Text.Json.Jsonata.CodeGeneration - 88.5%
Corvus.Text.Json.JsonLogic - 95.1%
Corvus.Text.Json.JsonLogic.CodeGeneration - 92.8%
Corvus.Text.Json.JsonPath - 95.2%
Corvus.Text.Json.JsonPath.CodeGeneration - 96.1%
Corvus.Text.Json.OpenApi - 95.7%
Corvus.Text.Json.OpenApi.CodeGeneration - 98.9%
|
Contributor
Contributor
Code Coverage Summary Report - Windows (net10.0)Summary
CoverageCorvus.Json.CodeGeneration - 80.3%
Corvus.Json.CodeGeneration.CSharp - 81.7%
Corvus.Json.ExtendedTypes - 74.9%
Corvus.Json.JsonReference - 76.7%
Corvus.Text.Json - 94.8%
Corvus.Text.Json.AsyncApi - 64.4%
Corvus.Text.Json.AsyncApi.Amqp - 0.6%
Corvus.Text.Json.AsyncApi.AzureServiceBus - 0%
Corvus.Text.Json.AsyncApi.CodeGeneration - 93.2%
Corvus.Text.Json.AsyncApi.Kafka - 0.6%
Corvus.Text.Json.AsyncApi.Mqtt - 0.9%
Corvus.Text.Json.AsyncApi.Nats - 0.4%
Corvus.Text.Json.AsyncApi.Polly - 100%
Corvus.Text.Json.AsyncApi.Testing - 96.6%
Corvus.Text.Json.AsyncApi.WebSocket - 0%
Corvus.Text.Json.CodeGeneration - 93.1%
Corvus.Text.Json.JMESPath - 93.8%
Corvus.Text.Json.JMESPath.CodeGeneration - 97.7%
Corvus.Text.Json.Jsonata - 91.6%
Corvus.Text.Json.Jsonata.CodeGeneration - 88.5%
Corvus.Text.Json.JsonLogic - 95.1%
Corvus.Text.Json.JsonLogic.CodeGeneration - 92.8%
Corvus.Text.Json.JsonPath - 95.2%
Corvus.Text.Json.JsonPath.CodeGeneration - 96.1%
Corvus.Text.Json.OpenApi - 95.7%
|
Contributor
Code Coverage Summary Report - Windows (net481)Summary
CoverageCorvus.Json.CodeGeneration - 80.4%
Corvus.Json.CodeGeneration.CSharp - 81.4%
Corvus.Json.ExtendedTypes - 72.6%
Corvus.Json.JsonReference - 73.8%
Corvus.Text.Json - 93.5%
Corvus.Text.Json.CodeGeneration - 86%
Corvus.Text.Json.JMESPath - 93.8%
Corvus.Text.Json.JMESPath.CodeGeneration - 97.7%
Corvus.Text.Json.Jsonata - 91.8%
Corvus.Text.Json.Jsonata.CodeGeneration - 88.6%
Corvus.Text.Json.JsonLogic - 95.1%
Corvus.Text.Json.JsonLogic.CodeGeneration - 92.8%
Corvus.Text.Json.JsonPath - 95.2%
Corvus.Text.Json.JsonPath.CodeGeneration - 96.1%
Corvus.Text.Json.Patch - 97.5%
Corvus.Text.Json.Toon - 88.2%
Corvus.Text.Json.Validator - 95.5%
Corvus.Text.Json.Yaml - 90.1%
Corvus.Toon.SystemTextJson - 90%
Corvus.Yaml.SystemTextJson - 88.6%
|
Contributor
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #819.
Problem
For a
number-typed schema with a zero-valued numeric bound (minimum,exclusiveMinimum,maximum, orexclusiveMaximum=0), validation was wrong for any value whose absolute magnitude lay in the open interval(0, 0.1)(e.g.0.05,0.083):minimum: 0/exclusiveMinimum: 0wrongly rejected such values.maximum: 0/exclusiveMaximum: 0wrongly accepted them.Values of
0, values>= 0.1, negatives, the same keywords with non-zero bounds, and allintegerschemas were unaffected.Root cause
The single shared comparator
JsonElementHelpers.CompareNormalizedJsonNumbersorders two normalized numbers by their effective length =significand length + exponent(the position of the most-significant digit). Zero normalizes to an empty significand withexponent = 0, giving it effective length0— the same band as values in[0.1, 1). Any value with magnitude< 0.1has effective length< 0, so it was ordered as if it were smaller than zero. Values>= 0.1share effective length0with zero and fall through to the correct digit-by-digit step — which is exactly why the failure band is|v| ∈ (0, 0.1).This is a runtime-library bug, not a codegen bug, which is why the generated comparator code was byte-for-byte identical across releases.
Fix
Detect the empty-significand (zero) case up front and order it explicitly — zero is less than every positive value and greater than every negative value — before the effective-length comparison:
One-site fix in the shared comparator used by generated models, the standalone evaluator, the dynamic
Validator, and JsonLogic. The only generation-time caller (IsUInt64) guardsexponent != 0and compares integers ≥ 1, so generated output is unchanged — no regeneration of generated code, recipes, or benchmarks is required.Tests (test-first — each fails before the fix, passes after)
NumericCoreOperationsTests.CompareNormalizedJsonNumbers— 12 new comparator-level rows: zero vs sub-0.1magnitudes in both directions (0.05,0.083,5e-50), plus sign-path and>= 0.1regression guards. 6 failed before the fix.ZeroNumericBoundTests— end-to-end dynamic-Validatorcoverage ofminimum/exclusiveMinimum/maximum/exclusiveMaximum=0against0,0.05,0.083,0.1,0.5,1, and negatives. 6 failed before the fix. (exclusiveMaximum: 0wasn't in the original report but is affected identically.)Verification
Corvus.Text.Json.Tests31229/31229 · SchemaTestSuite min/max (model-gen path) 185/185 · EvaluatorTestSuite min/max (standalone-evaluator path) 185/185 ·Validator.Tests94/94 · JsonLogic 878/878 · Numerics 1233/1233 (net10.0; net481 runs in CI on Windows — fix is pure managed code with no TFM branches).VERSIONHISTORY.mdupdated to V5.1.18.🤖 Generated with Claude Code