From c772cab766bf29eb94b98500049875a5d4ab495e Mon Sep 17 00:00:00 2001 From: Aidar Samerkhanov <aidar.samerkhanov@memgraph.io> Date: Fri, 12 Jan 2024 11:32:34 +0300 Subject: [PATCH] ToString function now returns double values with precision 15 (#1576) The DoubleToString function has been updated to handle higher precision doubles correctly. The unnecessary string length restriction has been removed, allowing the function to convert the full double value without prematurely truncating it. This change ensures that the string representation of doubles is more accurate, especially for very large or very small numbers. Unit tests have been added to verify the correct behavior for a range of double values. --- .../interpret/awesome_memgraph_functions.cpp | 2 +- src/utils/string.hpp | 29 ++++++++++++++++++- tests/unit/query_expression_evaluator.cpp | 7 +++-- tests/unit/utils_string.cpp | 21 +++++++++++++- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/query/interpret/awesome_memgraph_functions.cpp b/src/query/interpret/awesome_memgraph_functions.cpp index 7e1a7290f..ece0aec78 100644 --- a/src/query/interpret/awesome_memgraph_functions.cpp +++ b/src/query/interpret/awesome_memgraph_functions.cpp @@ -957,7 +957,7 @@ TypedValue ToString(const TypedValue *args, int64_t nargs, const FunctionContext return TypedValue(std::to_string(arg.ValueInt()), ctx.memory); } if (arg.IsDouble()) { - return TypedValue(std::to_string(arg.ValueDouble()), ctx.memory); + return TypedValue(memgraph::utils::DoubleToString(arg.ValueDouble()), ctx.memory); } if (arg.IsDate()) { return TypedValue(arg.ValueDate().ToString(), ctx.memory); diff --git a/src/utils/string.hpp b/src/utils/string.hpp index 833c158c8..8593fc57f 100644 --- a/src/utils/string.hpp +++ b/src/utils/string.hpp @@ -1,4 +1,4 @@ -// Copyright 2023 Memgraph Ltd. +// Copyright 2024 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source @@ -17,6 +17,7 @@ #include <charconv> #include <cstdint> #include <cstring> +#include <iomanip> #include <iostream> #include <iterator> #include <random> @@ -459,4 +460,30 @@ inline std::string_view Substr(const std::string_view string, size_t pos = 0, si return string.substr(pos, len); } +/** + * Convert a double value to a string representation. + * Precision of converted value is 16. + * Function also removes trailing zeros after the dot. + * + * @param value The double value to be converted. + * + * @return The string representation of the double value. + * + * @throws None + */ +inline std::string DoubleToString(const double value) { + static const int PRECISION = 15; + + std::stringstream ss; + ss << std::setprecision(PRECISION) << std::fixed << value; + auto sv = ss.view(); + + // Because of setprecision and fixed manipulator we are guaranteed to have the dot + sv = sv.substr(0, sv.find_last_not_of('0') + 1); + if (sv.ends_with('.')) { + sv = sv.substr(0, sv.size() - 1); + } + return std::string(sv); +} + } // namespace memgraph::utils diff --git a/tests/unit/query_expression_evaluator.cpp b/tests/unit/query_expression_evaluator.cpp index c070aaa32..b2a7c1f7a 100644 --- a/tests/unit/query_expression_evaluator.cpp +++ b/tests/unit/query_expression_evaluator.cpp @@ -2075,9 +2075,10 @@ TYPED_TEST(FunctionTest, ToStringInteger) { } TYPED_TEST(FunctionTest, ToStringDouble) { - EXPECT_EQ(this->EvaluateFunction("TOSTRING", -42.42).ValueString(), "-42.420000"); - EXPECT_EQ(this->EvaluateFunction("TOSTRING", 0.0).ValueString(), "0.000000"); - EXPECT_EQ(this->EvaluateFunction("TOSTRING", 238910.2313217).ValueString(), "238910.231322"); + EXPECT_EQ(this->EvaluateFunction("TOSTRING", -42.42).ValueString(), "-42.420000000000002"); + EXPECT_EQ(this->EvaluateFunction("TOSTRING", 0.0).ValueString(), "0"); + EXPECT_EQ(this->EvaluateFunction("TOSTRING", 238910.2313217).ValueString(), "238910.231321700004628"); + EXPECT_EQ(this->EvaluateFunction("TOSTRING", 238910.23132171234).ValueString(), "238910.231321712344652"); } TYPED_TEST(FunctionTest, ToStringBool) { diff --git a/tests/unit/utils_string.cpp b/tests/unit/utils_string.cpp index cefe57a6a..fdf64ae9f 100644 --- a/tests/unit/utils_string.cpp +++ b/tests/unit/utils_string.cpp @@ -1,4 +1,4 @@ -// Copyright 2023 Memgraph Ltd. +// Copyright 2024 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source @@ -171,3 +171,22 @@ TEST(String, Substr) { EXPECT_EQ(Substr(string, string.size() - 1, 1), string.substr(string.size() - 1, 1)); EXPECT_EQ(Substr(string, string.size() - 1, 2), string.substr(string.size() - 1, 2)); } + +TEST(String, DoubleToString) { + EXPECT_EQ(DoubleToString(0), "0"); + EXPECT_EQ(DoubleToString(1), "1"); + EXPECT_EQ(DoubleToString(1234567890123456), "1234567890123456"); + EXPECT_EQ(DoubleToString(static_cast<double>(12345678901234567)), "12345678901234568"); + EXPECT_EQ(DoubleToString(0.5), "0.5"); + EXPECT_EQ(DoubleToString(1.0), "1"); + EXPECT_EQ(DoubleToString(5.8), "5.8"); + EXPECT_EQ(DoubleToString(1.01234000), "1.01234"); + EXPECT_EQ(DoubleToString(1.036837585345), "1.036837585345"); + EXPECT_EQ(DoubleToString(103.6837585345), "103.683758534500001"); + EXPECT_EQ(DoubleToString(1.01234567890123456789), "1.012345678901235"); + EXPECT_EQ(DoubleToString(1234567.01234567891234567), "1234567.012345678871498"); + EXPECT_EQ(DoubleToString(0.00001), "0.00001"); + EXPECT_EQ(DoubleToString(0.00000000000001), "0.00000000000001"); + EXPECT_EQ(DoubleToString(0.000000000000001), "0.000000000000001"); + EXPECT_EQ(DoubleToString(0.0000000000000001), "0"); +}