From 3e2fc703644174a691c4f7e743ae0aa700561582 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 9 Jan 2025 08:36:07 -0500 Subject: [PATCH 01/18] Expand the basics section --- doc/decimal.adoc | 1 + doc/decimal/basics.adoc | 85 +++++++++++++++++++++++++++++++++++++++ doc/decimal/overview.adoc | 31 -------------- 3 files changed, 86 insertions(+), 31 deletions(-) create mode 100644 doc/decimal/basics.adoc diff --git a/doc/decimal.adoc b/doc/decimal.adoc index b5e0dd5cb..19e79a83e 100644 --- a/doc/decimal.adoc +++ b/doc/decimal.adoc @@ -18,6 +18,7 @@ https://www.boost.org/LICENSE_1_0.txt Matt Borland and Chris Kormanyos include::decimal/overview.adoc[] +include::decimal/basics.adoc[] include::decimal/api_reference.adoc[] include::decimal/generic_decimal.adoc[] include::decimal/decimal32.adoc[] diff --git a/doc/decimal/basics.adoc b/doc/decimal/basics.adoc new file mode 100644 index 000000000..b0fcf9540 --- /dev/null +++ b/doc/decimal/basics.adoc @@ -0,0 +1,85 @@ +//// +Copyright 2025 Matt Borland +Distributed under the Boost Software License, Version 1.0. +https://www.boost.org/LICENSE_1_0.txt +//// + +[#basics] += Basic Usage +:idprefix: basics_ + +== Construction of Decimal Types + +Every decimal type can be constructed in a few ways: + +1) The parameters to the constructor are like so: + +[source, c++] +---- +template +constexpr decimal32(T coeff, T2 exp, bool sign = false) noexcept; +---- + +Where types `T` and `T2` are integral types (signed and unsigned are both allowed). +Lastly the sign follows the convention of `signbit` where `false` is positive and `true` is negative. +If both a negative coefficient and a sign are passed then the resulting decimal number will be negative. +The final number constructed is in the form coeff x 10^exp. + +[souce, c++] +---- +boost::decimal::decimal32 a {1, 0}; // constructs 1 +boost::decimal::decimal32 b {-2, 0}; // constructs -2 +boost::decimal::decimal32 c {2, 0, true}; // Also constructs -2 +boost::decimal::decimal32 d {-2, 0, true}; // Also constructs -2 +boost::decimal::decimal32 e {5, 5}; // constructs 5x10^5 +boost::decimal::decimal32 f {1234, -3} // constructs 1.234 or 1234x10^-3 +---- + +2) A decimal number can be explicitly or implicitly constructed from an integer. +For example: + +[source, c++] +---- +boost::decimal::decimal64 g = 1; +boost::decimal::decimal32 h {-4}; +---- + +3) A decimal number can only be explicitly constructed from a floating point type. +For example: + +[source, c++] +---- +boost::decimal::decimal128 pi {3.14}; +---- + +NOTE: Due to the differences in decimal and binary floating point numbers there may be a difference in the resulting representation in decimal format, and thus it is not recommended to construct from binary floating point numbers + +== Using the Library + +The entire library should be accessed using the convince header ``. +A short example of the basic usage: + +[source, c++] +---- +#include +#include +#include + +int main() +{ + using namespace boost::decimal; + + // Outputs 0.30000000000000004 + std::cout << std::setprecision(17) << 0.1 + 0.2; + + // Construct the two decimal values + constexpr decimal64 a {1, -1}; // 1e-1 or 0.1 + constexpr decimal64 b {2, -1}; // 2e-1 or 0.2 + + // Outputs 0.30000000000000000 + std::cout << a + b << std::endl; + + return 0; +} + +---- diff --git a/doc/decimal/overview.adoc b/doc/decimal/overview.adoc index f27c017ad..95f44d3e7 100644 --- a/doc/decimal/overview.adoc +++ b/doc/decimal/overview.adoc @@ -40,34 +40,3 @@ as well as emulated PPC64LE and STM32 using QEMU with the following compilers: Tested on https://github.com/cppalliance/decimal/actions[Github Actions] and https://drone.cpp.al/cppalliance/decimal[Drone]. Coverage can be found on https://app.codecov.io/gh/cppalliance/decimal[Codecov]. - -== Basic Usage - -The entire library should be accessed using the convince header ``. -A short example of the basic usage: - -[source, c++] ----- -#include -#include -#include - -int main() -{ - using namespace boost::decimal; - - // Outputs 0.30000000000000004 - std::cout << std::setprecision(17) << 0.1 + 0.2; - - // Construct the two decimal values - constexpr decimal64 a {1, -1}; // 1e-1 or 0.1 - constexpr decimal64 b {2, -1}; // 2e-1 or 0.2 - - // Outputs 0.30000000000000000 - std::cout << a + b << std::endl; - - return 0; -} - ----- - From 14aad5fc24300d91f4cf9dc179c64944d122ddf7 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 9 Jan 2025 14:12:07 -0500 Subject: [PATCH 02/18] Address review comments --- doc/decimal/basics.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/decimal/basics.adoc b/doc/decimal/basics.adoc index b0fcf9540..81d0a7907 100644 --- a/doc/decimal/basics.adoc +++ b/doc/decimal/basics.adoc @@ -23,7 +23,7 @@ constexpr decimal32(T coeff, T2 exp, bool sign = false) noexcept; Where types `T` and `T2` are integral types (signed and unsigned are both allowed). Lastly the sign follows the convention of `signbit` where `false` is positive and `true` is negative. If both a negative coefficient and a sign are passed then the resulting decimal number will be negative. -The final number constructed is in the form coeff x 10^exp. +The final number constructed is in the form (sign ? -1 : 1) x coeff x 10^exp. [souce, c++] ---- @@ -70,7 +70,7 @@ int main() using namespace boost::decimal; // Outputs 0.30000000000000004 - std::cout << std::setprecision(17) << 0.1 + 0.2; + std::cout << std::setprecision(17) << 0.1 + 0.2 << std::endl; // Construct the two decimal values constexpr decimal64 a {1, -1}; // 1e-1 or 0.1 From 6db7051f06b299484029e4df6683b3d9965a5197 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 9 Jan 2025 17:21:46 -0500 Subject: [PATCH 03/18] Address review comments --- doc/decimal/basics.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/decimal/basics.adoc b/doc/decimal/basics.adoc index 81d0a7907..001e862f1 100644 --- a/doc/decimal/basics.adoc +++ b/doc/decimal/basics.adoc @@ -23,7 +23,7 @@ constexpr decimal32(T coeff, T2 exp, bool sign = false) noexcept; Where types `T` and `T2` are integral types (signed and unsigned are both allowed). Lastly the sign follows the convention of `signbit` where `false` is positive and `true` is negative. If both a negative coefficient and a sign are passed then the resulting decimal number will be negative. -The final number constructed is in the form (sign ? -1 : 1) x coeff x 10^exp. +The final number constructed is in the form (sign || coeff < 0 ? -1 : 1) x abs(coeff) x 10^exp. [souce, c++] ---- From 17e5adb7560beb59a82008c355af7116d1adb703 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 15 Jan 2025 08:19:30 -0500 Subject: [PATCH 04/18] Remove domain error verbiage for quantexp --- doc/decimal/cmath.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/decimal/cmath.adoc b/doc/decimal/cmath.adoc index b874fb8c1..bc8ac8b20 100644 --- a/doc/decimal/cmath.adoc +++ b/doc/decimal/cmath.adoc @@ -413,7 +413,7 @@ constexpr int quantexp128(decimal128 x) noexcept; Effects: if x is finite, returns its quantum exponent. -Otherwise, a domain error occurs and `INT_MIN` is returned. +Otherwise, `INT_MIN` is returned. === quantized From 7ad9836ec41e66cd0420eeee0cb65b20d660472e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 15 Jan 2025 08:51:20 -0500 Subject: [PATCH 05/18] Update jamfile --- fuzzing/Jamfile | 63 +++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/fuzzing/Jamfile b/fuzzing/Jamfile index 4739898ac..31dda6a58 100644 --- a/fuzzing/Jamfile +++ b/fuzzing/Jamfile @@ -1,27 +1,50 @@ -# -# Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) # Copyright (c) 2024 Matt Borland +# Copyright (c) 2025 Alexander Grund # -# Distributed under the Boost Software License, Version 1.0. (See accompanying -# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -# +# Distributed under the Boost Software License, Version 1.0. +# https://www.boost.org/LICENSE_1_0.txt. + import common ; +import path ; +import python ; import regex ; +import toolset ; + +path-constant HERE : . ; local all_fuzzers = [ regex.replace-list [ glob "fuzz_*.cpp" ] : ".cpp" : "" ] ; +if ! [ python.configured ] +{ + using python ; +} + +.make-corpus-script = $(HERE)/make-corpus.py ; + +rule make-corpus ( target : sources + : properties * ) +{ + RUNNER on $(target) = [ path.native $(.make-corpus-script) ] ; +} +actions make-corpus +{ + "$(PYTHON:E=python)" "$(RUNNER)" $(<) $(>) +} +toolset.flags $(__name__).make-corpus PYTHON ; + for local fuzzer in $(all_fuzzers) { - # These two fuzzers are the most complex ones. The rest are really - # simple, so less time is enough - local fuzz_time = 30 ; + local fuzz_time = 60 ; + local corpus = /tmp/corpus/$(fuzzer) ; + local min_corpus = /tmp/mincorpus/$(fuzzer) ; + local seed_corpus = $(HERE)/seedcorpus/$(fuzzer) ; + local seed_files = [ glob "$(seed_corpus)/*" ] ; # Create the output corpus directories - make /tmp/corpus/$(fuzzer) : : common.MkDir ; - make /tmp/mincorpus/$(fuzzer) : : common.MkDir ; + make $(corpus) : $(seed_files) : make-corpus ; + make $(min_corpus) : : common.MkDir ; # Build the fuzzer exe $(fuzzer) @@ -36,31 +59,21 @@ for local fuzzer in $(all_fuzzers) -fsanitize=fuzzer ; - # Make sure that any old crashes are run without problems - local old_crashes = [ glob-tree-ex old_crashes/$(fuzzer) : * ] ; - if $(old_crashes) - { - run $(fuzzer) - : target-name $(fuzzer)-old-crashes - : input-files [ SORT $(old_crashes) ] - ; - } - # Run the fuzzer for a short while run $(fuzzer) - : "seedcorpus/$(fuzzer) -max_total_time=$(fuzz_time)" + : "$(corpus) -max_total_time=$(fuzz_time)" : target-name $(fuzzer)-fuzzing : requirements - /tmp/corpus/$(fuzzer) + $(corpus) ; # Minimize the corpus run $(fuzzer) - : "/tmp/mincorpus/$(fuzzer) /tmp/corpus/$(fuzzer) -merge=1" + : "$(min_corpus) $(corpus) -merge=1" : target-name $(fuzzer)-minimize-corpus : requirements $(fuzzer)-fuzzing - /tmp/corpus/$(fuzzer) - /tmp/mincorpus/$(fuzzer) + $(corpus) + $(min_corpus) ; } From b9788570d3f45f3f9365f2e617bb7afa1b84c596 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 15 Jan 2025 08:52:12 -0500 Subject: [PATCH 06/18] Add Make corpus python file --- fuzzing/make-corpus.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 fuzzing/make-corpus.py diff --git a/fuzzing/make-corpus.py b/fuzzing/make-corpus.py new file mode 100644 index 000000000..93d6d0da2 --- /dev/null +++ b/fuzzing/make-corpus.py @@ -0,0 +1,32 @@ +#!/bin/env python + +# Copyright (c) 2025 Alexander Grund +# Distributed under the Boost Software License, Version 1.0. +# https://www.boost.org/LICENSE_1_0.txt. + +import os +import sys + +def get_samples(input_files): + for file_name in input_files: + if not os.path.isfile(file_name): + raise RuntimeError("Not a file: " + file_name) + with open(file_name, 'r') as input_file: + yield from input_file + + +def process_files(output_folder, input_files): + if not os.path.exists(output_folder): + os.makedirs(output_folder) + + for i, sample in enumerate(get_samples(input_files)): + with open(os.path.join(output_folder, str(i) + ".txt"), 'w') as output_file: + output_file.write(sample) + + +if __name__ == "__main__": + if len(sys.argv) < 3: + print("Usage: python script.py [ ...]") + sys.exit(1) + + process_files(output_folder=sys.argv[1], input_files=sys.argv[2:]) From 6e99976c5eb8835a8795479e3a7b0ca083d7847a Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 15 Jan 2025 09:21:07 -0500 Subject: [PATCH 07/18] Add note on normalization in charconv to docs --- doc/decimal/charconv.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/decimal/charconv.adoc b/doc/decimal/charconv.adoc index 2ae869620..9d1d83e4f 100644 --- a/doc/decimal/charconv.adoc +++ b/doc/decimal/charconv.adoc @@ -143,6 +143,8 @@ BOOST_DECIMAL_CONSTEXPR std::to_chars_result to_chars(char* first, char* last, D } //namespace boost ---- +All `to_chars` functions ignore the effects of cohorts, and instead output normalized values. + NOTE: `BOOST_DECIMAL_CONSTEXPR` is defined if: - `_MSC_FULL_VER` >= 192528326 From 158b4916bce8ba621c45f85d0c8b794bfd0fad52 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 15 Jan 2025 14:15:48 -0500 Subject: [PATCH 08/18] Fix typo --- doc/decimal/basics.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/decimal/basics.adoc b/doc/decimal/basics.adoc index 001e862f1..f501157b2 100644 --- a/doc/decimal/basics.adoc +++ b/doc/decimal/basics.adoc @@ -56,7 +56,7 @@ NOTE: Due to the differences in decimal and binary floating point numbers there == Using the Library -The entire library should be accessed using the convince header ``. +The entire library should be accessed using the convenience header ``. A short example of the basic usage: [source, c++] From ee92e743e08857a8fd97b79272bc2ea612b1ef09 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 15 Jan 2025 15:17:20 -0500 Subject: [PATCH 09/18] Fix missing class decimalXX { --- doc/decimal/decimal128.adoc | 4 ++++ doc/decimal/decimal128_fast.adoc | 4 ++++ doc/decimal/decimal32.adoc | 4 ++++ doc/decimal/decimal32_fast.adoc | 4 ++++ doc/decimal/decimal64.adoc | 4 ++++ doc/decimal/decimal64_fast.adoc | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/doc/decimal/decimal128.adoc b/doc/decimal/decimal128.adoc index 9555352c2..6b1950a5b 100644 --- a/doc/decimal/decimal128.adoc +++ b/doc/decimal/decimal128.adoc @@ -28,6 +28,8 @@ The encoding of Decimal128 is in the <>. namespace boost { namespace decimal { +class decimal128 { + // Paragraph numbers are from ISO/IEC DTR 24733 // 3.2.4.1 construct/copy/destroy @@ -85,6 +87,8 @@ explicit constexpr operator std::bfloat16_t() const noexcept; explicit constexpr operator decimal32() const noexcept; explicit constexpr operator decimal64() const noexcept; +}; // class decimal128 + } //namespace decimal } //namespace boost diff --git a/doc/decimal/decimal128_fast.adoc b/doc/decimal/decimal128_fast.adoc index 8a24add07..8b0496831 100644 --- a/doc/decimal/decimal128_fast.adoc +++ b/doc/decimal/decimal128_fast.adoc @@ -28,6 +28,8 @@ As is often the case this trades space for time by having greater storage width namespace boost { namespace decimal { +class decimal128_fast { + // Paragraph numbers are from ISO/IEC DTR 24733 // 3.2.4.1 construct/copy/destroy @@ -85,6 +87,8 @@ explicit constexpr operator std::bfloat16_t() const noexcept; explicit constexpr operator decimal32() const noexcept; explicit constexpr operator decimal64() const noexcept; +}; // class decimal128_fast + } //namespace decimal } //namespace boost diff --git a/doc/decimal/decimal32.adoc b/doc/decimal/decimal32.adoc index 9cb9ede38..7c006b8f5 100644 --- a/doc/decimal/decimal32.adoc +++ b/doc/decimal/decimal32.adoc @@ -28,6 +28,8 @@ The encoding of Decimal32 is in the <>. namespace boost { namespace decimal { +class decimal32 { + // Paragraph numbers are from ISO/IEC DTR 24733 // 3.2.2.1 construct/copy/destroy @@ -85,6 +87,8 @@ explicit constexpr operator std::bfloat16_t() const noexcept; explicit constexpr operator decimal64() const noexcept; explicit constexpr operator decimal128() const noexcept; +}; // class decimal32 + } //namespace decimal } //namespace boost diff --git a/doc/decimal/decimal32_fast.adoc b/doc/decimal/decimal32_fast.adoc index c1e8af44e..f98ed93b1 100644 --- a/doc/decimal/decimal32_fast.adoc +++ b/doc/decimal/decimal32_fast.adoc @@ -28,6 +28,8 @@ As is often the case this trades space for time by having greater storage width namespace boost { namespace decimal { +class decimal32_fast { + // Paragraph numbers are from ISO/IEC DTR 24733 // 3.2.2.1 construct/copy/destroy @@ -85,6 +87,8 @@ explicit constexpr operator std::bfloat16_t() const noexcept; explicit constexpr operator decimal64() const noexcept; explicit constexpr operator decimal128() const noexcept; +}; // class decimal32_fast + } //namespace decimal } //namespace boost diff --git a/doc/decimal/decimal64.adoc b/doc/decimal/decimal64.adoc index 390ca2f8c..59a48619c 100644 --- a/doc/decimal/decimal64.adoc +++ b/doc/decimal/decimal64.adoc @@ -28,6 +28,8 @@ The encoding of Decimal64 is in the <>. namespace boost { namespace decimal { +class decimal64 { + // Paragraph numbers are from ISO/IEC DTR 24733 // 3.2.3.1 construct/copy/destroy @@ -85,6 +87,8 @@ explicit constexpr operator std::bfloat16_t() const noexcept; explicit constexpr operator decimal32() const noexcept; explicit constexpr operator decimal128() const noexcept; +}; // class decimal64 + } //namespace decimal } //namespace boost diff --git a/doc/decimal/decimal64_fast.adoc b/doc/decimal/decimal64_fast.adoc index 99919ded3..d7bf2efb4 100644 --- a/doc/decimal/decimal64_fast.adoc +++ b/doc/decimal/decimal64_fast.adoc @@ -28,6 +28,8 @@ As is often the case this trades space for time by having greater storage width namespace boost { namespace decimal { +class decimal64_fast { + // Paragraph numbers are from ISO/IEC DTR 24733 // 3.2.3.1 construct/copy/destroy @@ -85,6 +87,8 @@ explicit constexpr operator std::bfloat16_t() const noexcept; explicit constexpr operator decimal32() const noexcept; explicit constexpr operator decimal128() const noexcept; +}; // class decimal64_fast + } //namespace decimal } //namespace boost From 538ac21cecd287ceb126b5a6ed74b5427d9e5841 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 17 Jan 2025 16:45:05 -0500 Subject: [PATCH 10/18] Add privately reported crash reproducer --- test/Jamfile | 1 + test/crash_report_1.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 test/crash_report_1.cpp diff --git a/test/Jamfile b/test/Jamfile index d67baeb4f..763173f66 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -45,6 +45,7 @@ project : requirements run-fail benchmarks.cpp ; run compare_dec128_and_fast.cpp ; compile-fail concepts_test.cpp ; +run crash_report_1.cpp ; run github_issue_426.cpp ; run github_issue_448.cpp ; run-fail github_issue_519.cpp ; diff --git a/test/crash_report_1.cpp b/test/crash_report_1.cpp new file mode 100644 index 000000000..bc8f4b162 --- /dev/null +++ b/test/crash_report_1.cpp @@ -0,0 +1,27 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include + +using namespace boost::decimal; + +int main() +{ + char buffer[64]{}; + const auto print_val {0.000001_df}; + + const auto r = boost::decimal::to_chars( + buffer, + buffer + sizeof(buffer), + print_val, + boost::decimal::chars_format::fixed + ); + *r.ptr = '\0'; + + BOOST_TEST_CSTR_EQ(buffer, "0.000001"); + + return boost::report_errors(); +} From 6ad83b84043d3937718f959870e1979296a1ff0c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 17 Jan 2025 17:00:27 -0500 Subject: [PATCH 11/18] Don't pass SIZE_T_MAX to memset --- include/boost/decimal/charconv.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/decimal/charconv.hpp b/include/boost/decimal/charconv.hpp index cc17272ce..e542c1b0f 100644 --- a/include/boost/decimal/charconv.hpp +++ b/include/boost/decimal/charconv.hpp @@ -569,7 +569,7 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_fixed_impl(char* first, char* last, const *first++ = '0'; return {first, std::errc()}; } - else if (num_leading_zeros > precision) + else if (precision != -1 && num_leading_zeros > precision) { *first++ = '0'; *first++ = '.'; From 23cfbfd5643b5219d4306bc66cffac3bd53a04bc Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 17 Jan 2025 17:01:28 -0500 Subject: [PATCH 12/18] Strip trailing zeros from significand fixed format with no precision --- include/boost/decimal/charconv.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/boost/decimal/charconv.hpp b/include/boost/decimal/charconv.hpp index e542c1b0f..662211856 100644 --- a/include/boost/decimal/charconv.hpp +++ b/include/boost/decimal/charconv.hpp @@ -542,16 +542,16 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_fixed_impl(char* first, char* last, const { append_trailing_zeros = true; } + } - // In general formatting we remove trailing 0s - if (fmt == chars_format::general) - { - - const auto zeros_removal {remove_trailing_zeros(significand)}; - significand = zeros_removal.trimmed_number; - exponent += static_cast(zeros_removal.number_of_removed_zeros); - num_dig -= static_cast(zeros_removal.number_of_removed_zeros); - } + // In general formatting we remove trailing 0s + // Same with unspecified precision fixed formatting + if ((precision == -1 && fmt == chars_format::fixed) || fmt == chars_format::general) + { + const auto zeros_removal {remove_trailing_zeros(significand)}; + significand = zeros_removal.trimmed_number; + exponent += static_cast(zeros_removal.number_of_removed_zeros); + num_dig -= static_cast(zeros_removal.number_of_removed_zeros); } // Make sure the result will fit in the buffer From 0328e0fe9632c41dd5843dc73cfc5a7676760971 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 17 Jan 2025 17:01:55 -0500 Subject: [PATCH 13/18] Exercise the no precision specified to_chars path --- test/test_to_chars.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/test_to_chars.cpp b/test/test_to_chars.cpp index 88eb681ab..e305c5c47 100644 --- a/test/test_to_chars.cpp +++ b/test/test_to_chars.cpp @@ -23,7 +23,7 @@ static constexpr auto N = static_cast(1024U >> 4U); // Number of tr #if !defined(BOOST_DECIMAL_DISABLE_CLIB) && !(defined(__GNUC__) && __GNUC__ >= 13 && !defined(__aarch64__)) template -void test_value(T val, const char* result, chars_format fmt, int precision = -1) +void test_value(T val, const char* result, chars_format fmt, int precision) { char buffer[256] {}; auto r = to_chars(buffer, buffer + sizeof(buffer), val, fmt, precision); @@ -32,6 +32,16 @@ void test_value(T val, const char* result, chars_format fmt, int precision = -1) BOOST_TEST_CSTR_EQ(result, buffer); } +template +void test_value(T val, const char* result, chars_format fmt) +{ + char buffer[256] {}; + auto r = to_chars(buffer, buffer + sizeof(buffer), val, fmt); + *r.ptr = '\0'; + BOOST_TEST(r); + BOOST_TEST_CSTR_EQ(result, buffer); +} + template void test_value(T val, const char* result) { From 49ba4bb5d6e030aa54a5c07a11c16ed9f85056f3 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 17 Jan 2025 20:48:54 -0500 Subject: [PATCH 14/18] Add additional powers of ten testing --- test/test_to_chars.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/test_to_chars.cpp b/test/test_to_chars.cpp index e305c5c47..ac60bf8b2 100644 --- a/test/test_to_chars.cpp +++ b/test/test_to_chars.cpp @@ -774,6 +774,25 @@ void test_777() test_value(value3, "-2111000000", chars_format::fixed, 0); } +template +void test_more_powers_10() +{ + test_value(T{1, -6}, "0.000001", chars_format::fixed); + test_value(T{1, -5}, "0.00001", chars_format::fixed); + test_value(T{1, -4}, "0.0001", chars_format::fixed); + test_value(T{1, -3}, "0.001", chars_format::fixed); + test_value(T{1, -2}, "0.01", chars_format::fixed); + test_value(T{1, -1}, "0.1", chars_format::fixed); + test_value(T{1, 0}, "1", chars_format::fixed); + test_value(T{1, 1}, "10", chars_format::fixed); + test_value(T{1, 2}, "100", chars_format::fixed); + test_value(T{1, 3}, "1000", chars_format::fixed); + test_value(T{1, 4}, "10000", chars_format::fixed); + test_value(T{1, 5}, "100000", chars_format::fixed); + test_value(T{1, 6}, "1000000", chars_format::fixed); + test_value(T{1, 7}, "10000000", chars_format::fixed); +} + int main() { test_non_finite_values(); @@ -884,6 +903,10 @@ int main() test_777(); test_777(); + test_more_powers_10(); + test_more_powers_10(); + test_more_powers_10(); + return boost::report_errors(); } From a965958f8e345533789542c2839cdf02ddd0170e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 17 Jan 2025 22:38:26 -0500 Subject: [PATCH 15/18] Add buffer and use constexpr memset --- include/boost/decimal/charconv.hpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/include/boost/decimal/charconv.hpp b/include/boost/decimal/charconv.hpp index 662211856..3ec1c90d2 100644 --- a/include/boost/decimal/charconv.hpp +++ b/include/boost/decimal/charconv.hpp @@ -573,21 +573,28 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_fixed_impl(char* first, char* last, const { *first++ = '0'; *first++ = '.'; - std::memset(first, '0', static_cast(precision)); + boost::decimal::detail::memset(first, '0', static_cast(precision)); return {first + precision, std::errc()}; } else { *first++ = '0'; *first++ = '.'; - std::memset(first, '0', static_cast(num_leading_zeros)); + boost::decimal::detail::memset(first, '0', static_cast(num_leading_zeros)); first += num_leading_zeros; // We can skip the rest if there's nothing more to do for the required precision if (significand == 0) { - std::memset(first, '0', static_cast(precision - num_leading_zeros)); - return {first + precision, std::errc()}; + if (precision - num_leading_zeros > 0) + { + boost::decimal::detail::memset(first, '0', static_cast(precision - num_leading_zeros)); + return {first + precision, std::errc()}; + } + else + { + return {first, std::errc()}; + } } } } From 8a6cf9259b033176e2289fb12b0c58c337ada25d Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 17 Jan 2025 22:38:34 -0500 Subject: [PATCH 16/18] Revert fuzz duration --- fuzzing/Jamfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzing/Jamfile b/fuzzing/Jamfile index 31dda6a58..7341af662 100644 --- a/fuzzing/Jamfile +++ b/fuzzing/Jamfile @@ -36,7 +36,7 @@ toolset.flags $(__name__).make-corpus PYTHON ; for local fuzzer in $(all_fuzzers) { - local fuzz_time = 60 ; + local fuzz_time = 30 ; local corpus = /tmp/corpus/$(fuzzer) ; local min_corpus = /tmp/mincorpus/$(fuzzer) ; local seed_corpus = $(HERE)/seedcorpus/$(fuzzer) ; From dccbab73d95e227c8a8987a1c1126c1da852e45d Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Sat, 18 Jan 2025 08:14:40 -0500 Subject: [PATCH 17/18] Ignore GCC-12 warning --- include/boost/decimal/detail/memcpy.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/decimal/detail/memcpy.hpp b/include/boost/decimal/detail/memcpy.hpp index 49f19e198..bfd8edd27 100644 --- a/include/boost/decimal/detail/memcpy.hpp +++ b/include/boost/decimal/detail/memcpy.hpp @@ -23,6 +23,7 @@ #if defined(__GNUC__) && __GNUC__ >= 10 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wstringop-overflow" +# pragma GCC diagnostic ignored "-Warray-bounds" # define BOOST_DECIMAL_STRINGOP_OVERFLOW_DISABLED #endif From f6f27a2c897b6d600287bd2d8d774f3420c7d396 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Sat, 18 Jan 2025 12:16:18 -0500 Subject: [PATCH 18/18] Revert "Merge pull request #789 from cppalliance/fuzzing" This reverts commit 5fdab9f695fdf70a446b4074c44aa8765ae3e1d8, reversing changes made to 113de3d1a748b153b77588a39cb50ec6b83ea5d1. --- fuzzing/Jamfile | 61 +++++++++++++++++------------------------- fuzzing/make-corpus.py | 32 ---------------------- 2 files changed, 24 insertions(+), 69 deletions(-) delete mode 100644 fuzzing/make-corpus.py diff --git a/fuzzing/Jamfile b/fuzzing/Jamfile index 7341af662..4739898ac 100644 --- a/fuzzing/Jamfile +++ b/fuzzing/Jamfile @@ -1,50 +1,27 @@ +# +# Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) # Copyright (c) 2024 Matt Borland -# Copyright (c) 2025 Alexander Grund # -# Distributed under the Boost Software License, Version 1.0. -# https://www.boost.org/LICENSE_1_0.txt. - +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# import common ; -import path ; -import python ; import regex ; -import toolset ; - -path-constant HERE : . ; local all_fuzzers = [ regex.replace-list [ glob "fuzz_*.cpp" ] : ".cpp" : "" ] ; -if ! [ python.configured ] -{ - using python ; -} - -.make-corpus-script = $(HERE)/make-corpus.py ; - -rule make-corpus ( target : sources + : properties * ) -{ - RUNNER on $(target) = [ path.native $(.make-corpus-script) ] ; -} -actions make-corpus -{ - "$(PYTHON:E=python)" "$(RUNNER)" $(<) $(>) -} -toolset.flags $(__name__).make-corpus PYTHON ; - for local fuzzer in $(all_fuzzers) { + # These two fuzzers are the most complex ones. The rest are really + # simple, so less time is enough local fuzz_time = 30 ; - local corpus = /tmp/corpus/$(fuzzer) ; - local min_corpus = /tmp/mincorpus/$(fuzzer) ; - local seed_corpus = $(HERE)/seedcorpus/$(fuzzer) ; - local seed_files = [ glob "$(seed_corpus)/*" ] ; # Create the output corpus directories - make $(corpus) : $(seed_files) : make-corpus ; - make $(min_corpus) : : common.MkDir ; + make /tmp/corpus/$(fuzzer) : : common.MkDir ; + make /tmp/mincorpus/$(fuzzer) : : common.MkDir ; # Build the fuzzer exe $(fuzzer) @@ -59,21 +36,31 @@ for local fuzzer in $(all_fuzzers) -fsanitize=fuzzer ; + # Make sure that any old crashes are run without problems + local old_crashes = [ glob-tree-ex old_crashes/$(fuzzer) : * ] ; + if $(old_crashes) + { + run $(fuzzer) + : target-name $(fuzzer)-old-crashes + : input-files [ SORT $(old_crashes) ] + ; + } + # Run the fuzzer for a short while run $(fuzzer) - : "$(corpus) -max_total_time=$(fuzz_time)" + : "seedcorpus/$(fuzzer) -max_total_time=$(fuzz_time)" : target-name $(fuzzer)-fuzzing : requirements - $(corpus) + /tmp/corpus/$(fuzzer) ; # Minimize the corpus run $(fuzzer) - : "$(min_corpus) $(corpus) -merge=1" + : "/tmp/mincorpus/$(fuzzer) /tmp/corpus/$(fuzzer) -merge=1" : target-name $(fuzzer)-minimize-corpus : requirements $(fuzzer)-fuzzing - $(corpus) - $(min_corpus) + /tmp/corpus/$(fuzzer) + /tmp/mincorpus/$(fuzzer) ; } diff --git a/fuzzing/make-corpus.py b/fuzzing/make-corpus.py deleted file mode 100644 index 93d6d0da2..000000000 --- a/fuzzing/make-corpus.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/env python - -# Copyright (c) 2025 Alexander Grund -# Distributed under the Boost Software License, Version 1.0. -# https://www.boost.org/LICENSE_1_0.txt. - -import os -import sys - -def get_samples(input_files): - for file_name in input_files: - if not os.path.isfile(file_name): - raise RuntimeError("Not a file: " + file_name) - with open(file_name, 'r') as input_file: - yield from input_file - - -def process_files(output_folder, input_files): - if not os.path.exists(output_folder): - os.makedirs(output_folder) - - for i, sample in enumerate(get_samples(input_files)): - with open(os.path.join(output_folder, str(i) + ".txt"), 'w') as output_file: - output_file.write(sample) - - -if __name__ == "__main__": - if len(sys.argv) < 3: - print("Usage: python script.py [ ...]") - sys.exit(1) - - process_files(output_folder=sys.argv[1], input_files=sys.argv[2:])