From 08a8c22576d5686a183462c2fe86a0913c1d9b91 Mon Sep 17 00:00:00 2001 From: Friday Date: Tue, 24 Feb 2026 04:14:18 +0000 Subject: [PATCH] Fix multipleOf with integer-valued float failing for large integers When multipleOf is a float with an integer value (e.g. 11.0), convert it to int before performing the check. This avoids floating-point precision loss for instances beyond 2**53, where float division produces incorrect results. Per JSON Schema spec, numbers with zero fractional parts are integers. Fixes #1159 --- jsonschema/_keywords.py | 6 ++++++ jsonschema/tests/test_validators.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/jsonschema/_keywords.py b/jsonschema/_keywords.py index f30f95419..1fde97f35 100644 --- a/jsonschema/_keywords.py +++ b/jsonschema/_keywords.py @@ -168,6 +168,12 @@ def multipleOf(validator, dB, instance, schema): if not validator.is_type(instance, "number"): return + # JSON Schema: numbers with zero fractional parts are integers. + # Convert integer-valued floats (e.g. 11.0) to int to avoid + # floating-point precision loss with large instances. + if isinstance(dB, float) and dB.is_integer(): + dB = int(dB) + if isinstance(dB, float): quotient = instance / dB try: diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py index 7d8a4c5cd..8021413c2 100644 --- a/jsonschema/tests/test_validators.py +++ b/jsonschema/tests/test_validators.py @@ -501,6 +501,23 @@ def test_multipleOf(self): ) self.assertEqual(message, "3 is not a multiple of 2") + def test_multipleOf_integer_valued_float(self): + """Integer-valued floats like 11.0 should behave like integers (#1159). + + Large integers beyond 2**53 lose precision in float division, + so multipleOf with an integer-valued float must use integer + arithmetic to avoid false negatives. + """ + instance = 9007199254740995 # 2**53 + 3, a multiple of 11 + # With integer divisor this passes + validators.Draft202012Validator( + {"type": "integer", "multipleOf": 11}, + ).validate(instance) + # With integer-valued float divisor this should also pass + validators.Draft202012Validator( + {"type": "integer", "multipleOf": 11.0}, + ).validate(instance) + def test_minItems(self): message = self.message_for(instance=[], schema={"minItems": 2}) self.assertEqual(message, "[] is too short")