diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d80827..9ef35cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,5 +106,4 @@ if (DRAGONBOX_ENABLE_SUBPROJECT) add_subdirectory("subproject/benchmark") add_subdirectory("subproject/meta") add_subdirectory("subproject/test") - add_subdirectory("subproject/simple") endif() \ No newline at end of file diff --git a/subproject/simple/CMakeLists.txt b/subproject/simple/CMakeLists.txt index 9a2cb9f..1be902e 100644 --- a/subproject/simple/CMakeLists.txt +++ b/subproject/simple/CMakeLists.txt @@ -1,9 +1,12 @@ -add_library(simple_dragonbox INTERFACE simple_dragonbox.h) -target_compile_features(simple_dragonbox INTERFACE cxx_std_17) - -add_executable(simple_dragonbox_test simple_dragonbox_test.cpp) -target_link_libraries(simple_dragonbox_test PRIVATE - simple_dragonbox - dragonbox::common - ryu::ryu -) +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +project(dragonbox_simple LANGUAGES CXX) + +add_library(dragonbox_simple INTERFACE include/simple_dragonbox.h) +add_library(dragonbox::simple ALIAS dragonbox_simple) + +target_include_directories(dragonbox_simple + INTERFACE + $) + +target_compile_features(dragonbox_simple INTERFACE cxx_std_17) \ No newline at end of file diff --git a/subproject/simple/simple_dragonbox.h b/subproject/simple/include/simple_dragonbox.h similarity index 100% rename from subproject/simple/simple_dragonbox.h rename to subproject/simple/include/simple_dragonbox.h diff --git a/subproject/simple/simple_dragonbox_test.cpp b/subproject/simple/simple_dragonbox_test.cpp deleted file mode 100644 index 12dc713..0000000 --- a/subproject/simple/simple_dragonbox_test.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2024 Junekey Jeon, Toby Bell -// -// The contents of this file may be used under the terms of -// the Apache License v2.0 with LLVM Exceptions. -// -// (See accompanying file LICENSE-Apache or copy at -// https://llvm.org/foundation/relicensing/LICENSE.txt) -// -// Alternatively, the contents of this file may be used under the terms of -// the Boost Software License, Version 1.0. -// (See accompanying file LICENSE-Boost or copy at -// https://www.boost.org/LICENSE_1_0.txt) -// -// Unless required by applicable law or agreed to in writing, this software -// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. - -#include "simple_dragonbox.h" -#include "random_float.h" -#include "ryu/ryu.h" - -#include -#include -#include - -static void reference_implementation(float x, char* buffer) { f2s_buffered(x, buffer); } -static void reference_implementation(double x, char* buffer) { d2s_buffered(x, buffer); } - -template -static bool test_simple_dragonbox(bool& success, Arg... arg) { - static constexpr char const* type_name = sizeof(Float) == 4 ? "float" : "double"; - static constexpr char const* compact = sizeof...(arg) ? "yes" : "no"; - static constexpr unsigned test_count = 10000000; - - std::cout << "uniform random test, type=" << type_name << " test_count=" << test_count - << " compact_cache=" << compact << " ...\n"; - - char buffer1[64]; - char buffer2[64]; - auto rg = generate_correctly_seeded_mt19937_64(); - for (std::size_t test_idx = 0; test_idx < test_count; ++test_idx) { - auto x = uniformly_randomly_generate_general_float(rg); - - // Check if the output is identical to the reference implementation (Ryu). - jkj::simple_dragonbox::to_chars(x, buffer1, arg...); - reference_implementation(x, buffer2); - - std::string_view view1(buffer1); - std::string_view view2(buffer2); - - if (view1 != view2) { - std::cout << "error detected, reference=" << buffer2 << " dragonbox=" << buffer1 << "\n"; - success = false; - } - } - - std::cout << "done.\n"; - - std::cout << "test all shorter interval cases, type=" << type_name << " compact_cache=" << compact - << " ... \n"; - - using format = jkj::simple_dragonbox::detail::float_format; - using carrier_uint = typename format::carrier_uint; - - for (int e = format::min_exponent; e <= format::max_exponent; ++e) { - // Compose a floating-point number - carrier_uint br = carrier_uint(e - format::exponent_bias) << format::significand_bits; - - Float x; - static_assert(sizeof(br) == sizeof(x)); - std::memcpy(&x, &br, sizeof(br)); - - jkj::simple_dragonbox::to_chars(x, buffer1, arg...); - reference_implementation(x, buffer2); - - std::string_view view1(buffer1); - std::string_view view2(buffer2); - - if (view1 != view2) { - std::cout << "error detected, reference=" << buffer2 << " dragonbox=" << buffer1 << "\n"; - success = false; - } - } - - std::cout << "done.\n"; - - return success; -} - -int main() { - bool success = true; - test_simple_dragonbox(success); - test_simple_dragonbox(success, jkj::simple_dragonbox::policy::cache::compact); - test_simple_dragonbox(success); - test_simple_dragonbox(success, jkj::simple_dragonbox::policy::cache::compact); - if (!success) - return -1; -} diff --git a/subproject/test/CMakeLists.txt b/subproject/test/CMakeLists.txt index 9c06346..c08ed88 100644 --- a/subproject/test/CMakeLists.txt +++ b/subproject/test/CMakeLists.txt @@ -16,6 +16,10 @@ if (NOT TARGET common) FetchContent_Declare(common SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../common") FetchContent_MakeAvailable(common) endif() +if (NOT TARGET simple) + FetchContent_Declare(simple SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../simple") + FetchContent_MakeAvailable(simple) +endif() if (NOT TARGET ryu) FetchContent_Declare(ryu SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../3rdparty/ryu") FetchContent_MakeAvailable(ryu) @@ -35,7 +39,7 @@ function(add_test NAME) add_executable(${NAME} source/${NAME}.cpp) - target_link_libraries(${NAME} PRIVATE ${dragonbox} dragonbox::common) + target_link_libraries(${NAME} PRIVATE ${dragonbox} dragonbox::common dragonbox::simple) if(TEST_RYU) target_link_libraries(${NAME} PRIVATE ryu::ryu) diff --git a/subproject/test/source/test_all_shorter_interval_cases.cpp b/subproject/test/source/test_all_shorter_interval_cases.cpp index 7515a68..309fe9f 100644 --- a/subproject/test/source/test_all_shorter_interval_cases.cpp +++ b/subproject/test/source/test_all_shorter_interval_cases.cpp @@ -1,4 +1,4 @@ -// Copyright 2020 Junekey Jeon +// Copyright 2020-2024 Junekey Jeon // // The contents of this file may be used under the terms of // the Apache License v2.0 with LLVM Exceptions. @@ -16,6 +16,7 @@ // KIND, either express or implied. #include "dragonbox/dragonbox_to_chars.h" +#include "simple_dragonbox.h" #include "ryu/ryu.h" #include @@ -26,8 +27,8 @@ static void reference_implementation(float x, char* buffer) { f2s_buffered(x, buffer); } static void reference_implementation(double x, char* buffer) { d2s_buffered(x, buffer); } -template -static bool test_all_shorter_interval_cases_impl(Args&&... args) { +template +static bool test_all_shorter_interval_cases_impl(TestTarget&& test_target) { using conversion_traits = jkj::dragonbox::default_float_bit_carrier_conversion_traits; using ieee754_format_info = typename conversion_traits::format; using carrier_uint = typename conversion_traits::carrier_uint; @@ -42,7 +43,7 @@ static bool test_all_shorter_interval_cases_impl(Args&&... args) { << ieee754_format_info::significand_bits; auto x = conversion_traits::carrier_to_float(br); - jkj::dragonbox::to_chars(x, buffer1, std::forward(args)...); + test_target(x, buffer1); reference_implementation(x, buffer2); std::string_view view1(buffer1); @@ -68,19 +69,49 @@ int main() { bool success = true; std::cout << "[Testing all shorter interval cases for binary32...]\n"; - success &= test_all_shorter_interval_cases_impl(); + success &= test_all_shorter_interval_cases_impl( + [](auto x, char* buffer) { jkj::dragonbox::to_chars(x, buffer); }); std::cout << "Done.\n\n\n"; - std::cout << "[Testing all shorter interval cases for binary32 with compressed cache...]\n"; - success &= test_all_shorter_interval_cases_impl(jkj::dragonbox::policy::cache::compact); + std::cout << "[Testing all shorter interval cases for binary32 (compact cache)...]\n"; + success &= test_all_shorter_interval_cases_impl([](auto x, char* buffer) { + jkj::dragonbox::to_chars(x, buffer, jkj::dragonbox::policy::cache::compact); + }); + std::cout << "Done.\n\n\n"; + + std::cout << "[Testing all shorter interval cases for binary32 (simplified impl)...]\n"; + success &= test_all_shorter_interval_cases_impl( + [](auto x, char* buffer) { jkj::simple_dragonbox::to_chars(x, buffer); }); + std::cout << "Done.\n\n\n"; + + std::cout + << "[Testing all shorter interval cases for binary32 (simplified impl, compact cache)...]\n"; + success &= test_all_shorter_interval_cases_impl([](auto x, char* buffer) { + jkj::simple_dragonbox::to_chars(x, buffer, jkj::simple_dragonbox::policy::cache::compact); + }); std::cout << "Done.\n\n\n"; std::cout << "[Testing all shorter interval cases for binary64...]\n"; - success &= test_all_shorter_interval_cases_impl(); + success &= test_all_shorter_interval_cases_impl( + [](auto x, char* buffer) { jkj::dragonbox::to_chars(x, buffer); }); + std::cout << "Done.\n\n\n"; + + std::cout << "[Testing all shorter interval cases for binary64 (compact cache)...]\n"; + success &= test_all_shorter_interval_cases_impl([](auto x, char* buffer) { + jkj::dragonbox::to_chars(x, buffer, jkj::dragonbox::policy::cache::compact); + }); + std::cout << "Done.\n\n\n"; + + std::cout << "[Testing all shorter interval cases for binary64 (simplified impl)...]\n"; + success &= test_all_shorter_interval_cases_impl( + [](auto x, char* buffer) { jkj::simple_dragonbox::to_chars(x, buffer); }); std::cout << "Done.\n\n\n"; - std::cout << "[Testing all shorter interval cases for binary64 with compressed cache...]\n"; - success &= test_all_shorter_interval_cases_impl(jkj::dragonbox::policy::cache::compact); + std::cout + << "[Testing all shorter interval cases for binary64 (simplified impl, compact cache)...]\n"; + success &= test_all_shorter_interval_cases_impl([](auto x, char* buffer) { + jkj::simple_dragonbox::to_chars(x, buffer, jkj::simple_dragonbox::policy::cache::compact); + }); std::cout << "Done.\n\n\n"; if (!success) { diff --git a/subproject/test/source/uniform_random_test.cpp b/subproject/test/source/uniform_random_test.cpp index 9f7fb14..aadada1 100644 --- a/subproject/test/source/uniform_random_test.cpp +++ b/subproject/test/source/uniform_random_test.cpp @@ -1,4 +1,4 @@ -// Copyright 2020 Junekey Jeon +// Copyright 2020-2024 Junekey Jeon // // The contents of this file may be used under the terms of // the Apache License v2.0 with LLVM Exceptions. @@ -16,6 +16,7 @@ // KIND, either express or implied. #include "dragonbox/dragonbox_to_chars.h" +#include "simple_dragonbox.h" #include "random_float.h" #include "ryu/ryu.h" @@ -26,9 +27,8 @@ static void reference_implementation(float x, char* buffer) { f2s_buffered(x, buffer); } static void reference_implementation(double x, char* buffer) { d2s_buffered(x, buffer); } -template -static bool uniform_random_test(std::size_t number_of_tests, TypenameString&& type_name_string, - Args&&... args) { +template +static bool uniform_random_test(std::size_t number_of_tests, TestTarget&& test_target) { char buffer1[64]; char buffer2[64]; auto rg = generate_correctly_seeded_mt19937_64(); @@ -37,7 +37,7 @@ static bool uniform_random_test(std::size_t number_of_tests, TypenameString&& ty auto x = uniformly_randomly_generate_general_float(rg); // Check if the output is identical to the reference implementation (Ryu). - jkj::dragonbox::to_chars(x, buffer1, std::forward(args)...); + test_target(x, buffer1); reference_implementation(x, buffer2); std::string_view view1(buffer1); @@ -51,48 +51,92 @@ static bool uniform_random_test(std::size_t number_of_tests, TypenameString&& ty } if (success) { - std::cout << "Uniform random test for " << type_name_string << " with " << number_of_tests - << " examples succeeded.\n"; + std::cout << "Uniform random test with " << number_of_tests << " examples succeeded.\n"; + } + else { + std::cout << "Error detected.\n"; } return success; } int main() { - constexpr bool run_float = true; constexpr std::size_t number_of_uniform_random_tests_float = 10000000; + constexpr bool run_float = true; + constexpr bool run_float_with_compact_cache = true; + constexpr bool run_simple_float = true; + constexpr bool run_simpl_float_with_compact_cache = true; - constexpr bool run_float_with_compressed_cache = true; - constexpr std::size_t number_of_uniform_random_tests_float_compressed = 10000000; - - constexpr bool run_double = true; constexpr std::size_t number_of_uniform_random_tests_double = 10000000; - - constexpr bool run_double_with_compressed_cache = true; - constexpr std::size_t number_of_uniform_random_tests_double_compressed = 10000000; + constexpr bool run_double = true; + constexpr bool run_double_with_compact_cache = true; + constexpr bool run_simple_double = true; + constexpr bool run_simple_double_with_compact_cache = true; bool success = true; if (run_float) { - std::cout << "[Testing uniformly randomly generated float inputs...]\n"; - success &= uniform_random_test(number_of_uniform_random_tests_float, "float"); + std::cout << "[Testing uniformly randomly generated binary32 inputs...]\n"; + success &= + uniform_random_test(number_of_uniform_random_tests_float, [](auto x, char* buffer) { + jkj::dragonbox::to_chars(x, buffer); + }); std::cout << "Done.\n\n\n"; } - if (run_float_with_compressed_cache) { - std::cout << "[Testing uniformly randomly generated float inputs with compressed cache...]\n"; - success &= uniform_random_test(number_of_uniform_random_tests_float_compressed, "float", - jkj::dragonbox::policy::cache::compact); + if (run_float_with_compact_cache) { + std::cout << "[Testing uniformly randomly generated binary32 inputs (compact cache)...]\n"; + success &= + uniform_random_test(number_of_uniform_random_tests_float, [](auto x, char* buffer) { + jkj::dragonbox::to_chars(x, buffer, jkj::dragonbox::policy::cache::compact); + }); + std::cout << "Done.\n\n\n"; + } + if (run_simple_float) { + std::cout << "[Testing uniformly randomly generated binary32 inputs (simplified impl)...]\n"; + success &= + uniform_random_test(number_of_uniform_random_tests_float, [](auto x, char* buffer) { + jkj::simple_dragonbox::to_chars(x, buffer); + }); + std::cout << "Done.\n\n\n"; + } + if (run_simpl_float_with_compact_cache) { + std::cout << "[Testing uniformly randomly generated binary32 inputs (simplified impl, compact " + "cache)...]\n"; + success &= uniform_random_test(number_of_uniform_random_tests_float, [](auto x, + char* buffer) { + jkj::simple_dragonbox::to_chars(x, buffer, jkj::simple_dragonbox::policy::cache::compact); + }); std::cout << "Done.\n\n\n"; } if (run_double) { - std::cout << "[Testing uniformly randomly generated double inputs...]\n"; - success &= uniform_random_test(number_of_uniform_random_tests_double, "double"); + std::cout << "[Testing uniformly randomly generated binary64 inputs...]\n"; + success &= uniform_random_test( + number_of_uniform_random_tests_double, + [](auto x, char* buffer) { jkj::dragonbox::to_chars(x, buffer); }); + std::cout << "Done.\n\n\n"; + } + if (run_double_with_compact_cache) { + std::cout << "[Testing uniformly randomly generated binary64 inputs (compact cache)...]\n"; + success &= uniform_random_test( + number_of_uniform_random_tests_double, [](auto x, char* buffer) { + jkj::dragonbox::to_chars(x, buffer, jkj::dragonbox::policy::cache::compact); + }); + std::cout << "Done.\n\n\n"; + } + if (run_simple_double) { + std::cout << "[Testing uniformly randomly generated binary64 inputs (simplified impl)...]\n"; + success &= uniform_random_test( + number_of_uniform_random_tests_double, + [](auto x, char* buffer) { jkj::simple_dragonbox::to_chars(x, buffer); }); std::cout << "Done.\n\n\n"; } - if (run_double_with_compressed_cache) { - std::cout << "[Testing uniformly randomly generated double inputs with compressed cache...]\n"; - success &= uniform_random_test(number_of_uniform_random_tests_double_compressed, - "double", jkj::dragonbox::policy::cache::compact); + if (run_simple_double_with_compact_cache) { + std::cout << "[Testing uniformly randomly generated binary64 inputs with (simplified impl, " + "compact cache)...]\n"; + success &= uniform_random_test(number_of_uniform_random_tests_double, [](auto x, + char* buffer) { + jkj::simple_dragonbox::to_chars(x, buffer, jkj::simple_dragonbox::policy::cache::compact); + }); std::cout << "Done.\n\n\n"; }