From a19d71ddcb891602606aa4a768fb76a2cf729b1d Mon Sep 17 00:00:00 2001 From: dhmemi Date: Thu, 14 Sep 2023 11:08:38 +0800 Subject: [PATCH] fix: validate multipleOf fails on float-point value --- .gitignore | 1 + src/json-validator.cpp | 10 +++++++--- test/CMakeLists.txt | 4 ++++ test/issue-293.cpp | 27 +++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 test/issue-293.cpp diff --git a/.gitignore b/.gitignore index aceaf81..bc9dd9f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ cmake-build-* venv env compile_commands.json +.vs/* diff --git a/src/json-validator.cpp b/src/json-validator.cpp index 77d701f..2021471 100644 --- a/src/json-validator.cpp +++ b/src/json-validator.cpp @@ -863,9 +863,13 @@ class numeric : public schema // multipleOf - if the remainder of the division is 0 -> OK bool violates_multiple_of(T x) const { - double res = std::remainder(x, multipleOf_.second); - double eps = std::nextafter(x, 0) - static_cast(x); - return std::fabs(res) > std::fabs(eps); + if constexpr (std::is_floating_point_v) { + auto multiple = x / multipleOf_.second; + auto error = std::abs((multiple - std::round(multiple)) * multipleOf_.second); + return error > std::numeric_limits::epsilon(); + } else { + return x % static_cast(multipleOf_.second) != 0; + } } void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0345b34..b461df6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -43,6 +43,10 @@ add_executable(issue-98 issue-98.cpp) target_link_libraries(issue-98 nlohmann_json_schema_validator) add_test(NAME issue-98-erase-exception-unknown-keywords COMMAND issue-98) +add_executable(issue-293 issue-293.cpp) +target_link_libraries(issue-293 nlohmann_json_schema_validator) +add_test(NAME issue-293-float-point-error COMMAND issue-293) + # Unit test for string format checks add_executable(string-format-check-test string-format-check-test.cpp) target_include_directories(string-format-check-test PRIVATE ${PROJECT_SOURCE_DIR}/src/) diff --git a/test/issue-293.cpp b/test/issue-293.cpp new file mode 100644 index 0000000..fb566e1 --- /dev/null +++ b/test/issue-293.cpp @@ -0,0 +1,27 @@ +#include "nlohmann/json-schema.hpp" + +int main(void) +{ + using nlohmann::json_schema::json_validator; + + json_validator({{"type", "number"}, {"multipleOf", 0.001}}).validate(0.3 - 0.2); + + json_validator({{"type", "number"}, {"multipleOf", 3.3}}).validate(8.0 - 1.4); + + json_validator({{"type", "number"}, {"multipleOf", 1000.01}}).validate((1000.03 - 0.02) * 15.0); + + int expect_exception = 2; + try { + json_validator({{"type", "number"}, {"multipleOf", 0.001}}).validate(0.3 - 0.2005); + } catch (const std::exception &ex) { + expect_exception--; + } + + try { + json_validator({{"type", "number"}, {"multipleOf", 1000.02}}).validate((1000.03 - 0.02) * 15.0); + } catch (const std::exception &ex) { + expect_exception--; + } + + return expect_exception; +}