diff --git a/include/internal/csv_writer.hpp b/include/internal/csv_writer.hpp index 2f02eaf1..8a8506ad 100644 --- a/include/internal/csv_writer.hpp +++ b/include/internal/csv_writer.hpp @@ -128,8 +128,8 @@ namespace internals { csv::enable_if_t::value && std::is_signed::value, int> = 0 > inline std::string to_string(T value) { - return (value >= 0) ? to_string((size_t)value) - : "-" + to_string((size_t)(value * -1)); + return (value >= 0) ? to_string(static_cast(value)) + : "-" + to_string(0ULL - static_cast(value)); } /** to_string() for floating point numbers */ diff --git a/tests/test_write_csv.cpp b/tests/test_write_csv.cpp index c9c8845b..7e06e2a3 100644 --- a/tests/test_write_csv.cpp +++ b/tests/test_write_csv.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -449,3 +450,38 @@ TEST_CASE("DataFrame - Write with Sparse Overlay", "[test_dataframe_sparse_overl REQUIRE(result.find("Everything is fine in production") != std::string::npos); // quote updated } //! [DataFrame Sparse Overlay Write Example] + +TEST_CASE("Signed integer conversion - extrema regression", "[test_convert_number][regression]") { + SECTION("Negative integers") { + // Negating the minimum signed value used to overflow before conversion. + const int min_int = (std::numeric_limits::min)(); + const long long min_long_long = (std::numeric_limits::min)(); + + REQUIRE(csv::internals::to_string(-1) == "-1"); + REQUIRE(csv::internals::to_string(min_int) == std::to_string(min_int)); + REQUIRE(csv::internals::to_string(min_long_long) == std::to_string(min_long_long)); + } + + SECTION("Non-negative integers") { + // Converting through size_t can truncate long long on 32-bit platforms. + const long long max_long_long = (std::numeric_limits::max)(); + + REQUIRE(csv::internals::to_string(0) == "0"); + REQUIRE(csv::internals::to_string(0LL) == "0"); + REQUIRE(csv::internals::to_string(max_long_long) == std::to_string(max_long_long)); + } +} + +TEST_CASE("CSV Writer - signed integer extrema regression", "[test_csv_tuple][regression]") { + // Exercise the public writer API so extrema stay intact in a mixed tuple. + const int min_int = (std::numeric_limits::min)(); + const long long min_long_long = (std::numeric_limits::min)(); + const long long max_long_long = (std::numeric_limits::max)(); + std::stringstream output, correct; + auto writer = make_csv_writer(output); + + writer << std::make_tuple(min_int, min_long_long, -1, 0, max_long_long); + correct << min_int << ',' << min_long_long << ",-1,0," << max_long_long << '\n'; + + REQUIRE(output.str() == correct.str()); +}