From 195ee4fa6efae0f9a08834c7e3e0bf740cc74486 Mon Sep 17 00:00:00 2001 From: "Steinke, Rico" Date: Wed, 28 Aug 2024 15:57:11 +0200 Subject: [PATCH 1/4] add function to calculate remainder for floats correctly without rounding errors and fix checking if a number is a multipleOf for floats --- src/languageservice/parser/jsonParser07.ts | 3 ++- src/languageservice/utils/math.ts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/languageservice/utils/math.ts diff --git a/src/languageservice/parser/jsonParser07.ts b/src/languageservice/parser/jsonParser07.ts index b4fdfe4b..00ea6b7d 100644 --- a/src/languageservice/parser/jsonParser07.ts +++ b/src/languageservice/parser/jsonParser07.ts @@ -27,6 +27,7 @@ import { isArrayEqual } from '../utils/arrUtils'; import { Node, Pair } from 'yaml'; import { safeCreateUnicodeRegExp } from '../utils/strings'; import { FilePatternAssociation } from '../services/yamlSchemaService'; +import { floatSafeRemainder } from '../utils/math'; const localize = nls.loadMessageBundle(); const MSG_PROPERTY_NOT_ALLOWED = 'Property {0} is not allowed.'; @@ -930,7 +931,7 @@ function validate( const val = node.value; if (isNumber(schema.multipleOf)) { - if (val % schema.multipleOf !== 0) { + if (floatSafeRemainder(val, schema.multipleOf) !== 0) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, severity: DiagnosticSeverity.Warning, diff --git a/src/languageservice/utils/math.ts b/src/languageservice/utils/math.ts new file mode 100644 index 00000000..8c304b3d --- /dev/null +++ b/src/languageservice/utils/math.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function floatSafeRemainder( + val: number, + step: number +): number { + const valDecCount = (val.toString().split('.')[1] || '').length; + const stepDecCount = (step.toString().split('.')[1] || '').length; + const decCount = Math.max(valDecCount, stepDecCount); + const valInt = parseInt(val.toFixed(decCount).replace('.', '')); + const stepInt = parseInt(step.toFixed(decCount).replace('.', '')); + return (valInt % stepInt) / Math.pow(10, decCount); +} \ No newline at end of file From 5a9a6b74c43e08b7b0f58952fffeab98ce4f3cc8 Mon Sep 17 00:00:00 2001 From: "Steinke, Rico" Date: Wed, 28 Aug 2024 16:03:24 +0200 Subject: [PATCH 2/4] fix formatting --- src/languageservice/utils/math.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/languageservice/utils/math.ts b/src/languageservice/utils/math.ts index 8c304b3d..705a8ae2 100644 --- a/src/languageservice/utils/math.ts +++ b/src/languageservice/utils/math.ts @@ -3,14 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export function floatSafeRemainder( - val: number, - step: number -): number { - const valDecCount = (val.toString().split('.')[1] || '').length; - const stepDecCount = (step.toString().split('.')[1] || '').length; - const decCount = Math.max(valDecCount, stepDecCount); - const valInt = parseInt(val.toFixed(decCount).replace('.', '')); - const stepInt = parseInt(step.toFixed(decCount).replace('.', '')); - return (valInt % stepInt) / Math.pow(10, decCount); -} \ No newline at end of file +export function floatSafeRemainder(val: number, step: number): number { + const valDecCount = (val.toString().split('.')[1] || '').length; + const stepDecCount = (step.toString().split('.')[1] || '').length; + const decCount = Math.max(valDecCount, stepDecCount); + const valInt = parseInt(val.toFixed(decCount).replace('.', '')); + const stepInt = parseInt(step.toFixed(decCount).replace('.', '')); + return (valInt % stepInt) / Math.pow(10, decCount); +} From 2a11a72aacaa87dbbc1e98a310fa3b838928e63b Mon Sep 17 00:00:00 2001 From: "Steinke, Rico" Date: Thu, 29 Aug 2024 11:37:17 +0200 Subject: [PATCH 3/4] add unit test --- test/jsonParser.test.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/jsonParser.test.ts b/test/jsonParser.test.ts index 0ef55a2e..5ec9bd83 100644 --- a/test/jsonParser.test.ts +++ b/test/jsonParser.test.ts @@ -1541,6 +1541,28 @@ describe('JSON Parser', () => { } }); + it('multipleOfFloat', function () { + const schema: JsonSchema.JSONSchema = { + type: 'array', + items: { + type: 'number', + multipleOf: 0.05, + }, + }; + { + const { textDoc, jsonDoc } = toDocument('[0.9]'); + const semanticErrors = jsonDoc.validate(textDoc, schema); + + assert.strictEqual(semanticErrors.length, 0); + } + { + const { textDoc, jsonDoc } = toDocument('[42.3222222]'); + const semanticErrors = jsonDoc.validate(textDoc, schema); + + assert.strictEqual(semanticErrors.length, 1); + } + }); + it('dependencies with array', function () { const schema: JsonSchema.JSONSchema = { type: 'object', From ce00309375380d7b67da4f47c26ce79b3d47a0b6 Mon Sep 17 00:00:00 2001 From: "Steinke, Rico" Date: Fri, 30 Aug 2024 09:24:11 +0200 Subject: [PATCH 4/4] rm copyright --- src/languageservice/utils/math.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/languageservice/utils/math.ts b/src/languageservice/utils/math.ts index 705a8ae2..36fe87e4 100644 --- a/src/languageservice/utils/math.ts +++ b/src/languageservice/utils/math.ts @@ -1,8 +1,3 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - export function floatSafeRemainder(val: number, step: number): number { const valDecCount = (val.toString().split('.')[1] || '').length; const stepDecCount = (step.toString().split('.')[1] || '').length;