├── src ├── single_header │ ├── footer.h │ ├── single_header.cpp │ ├── standard_includes.h │ ├── fixed_point_includes.h │ ├── header.h │ └── CMakeLists.txt ├── test │ ├── main.cpp │ ├── fixed_point_math_Q15.cpp │ ├── fixed_point_math_Q0.cpp │ ├── fixed_point_math_Q1.cpp │ ├── fixed_point_math_Q31.cpp │ ├── fixed_point_math.cpp │ ├── fixed_point_built_in.cpp │ ├── common.cpp │ ├── number_test.cpp │ ├── fixed_point_native_integer.cpp │ ├── fixed_point_saturated_integer.cpp │ ├── readme.cpp │ ├── glm.cpp │ ├── zero_cost_free_functions.cpp │ ├── precise_fixed_point.cpp │ ├── fixed_point_throwing_integer.cpp │ ├── p0554.cpp │ ├── zero_cost_square.cpp │ ├── precise_safe_elastic_integer.cpp │ ├── precise_elastic_integer.cpp │ ├── utils.cpp │ ├── precise_safe_elastic_fixed_point.cpp │ ├── snippets.cpp │ ├── num_traits.cpp │ ├── fixed_point_math_common.h │ ├── zero_cost_average.cpp │ ├── const_integer.cpp │ ├── p0675.cpp │ ├── p0037.cpp │ ├── p0381.cpp │ ├── boost.simd.cpp │ ├── safe_elastic_integer.cpp │ ├── precise_integer.cpp │ ├── CMakeLists.txt │ ├── index.cpp │ ├── cppnow2017.cpp │ ├── multiprecision.cpp │ └── boost.multiprecision.cpp ├── common │ ├── sample_functions.h │ ├── fixed_point.natvis │ └── common.cmake └── benchmark │ ├── report.py │ ├── CMakeLists.txt │ └── review.py ├── doc ├── presentations │ ├── 2016-09-19_CppCon2016.pdf │ ├── 2016-06-08_ACCU_Silicon_Valley.pdf │ └── 2017-05-16_CppNow2017 │ │ └── Composite Arithmetic Types Are _ the + of Their Parts.pdf ├── CMakeLists.txt ├── index.md └── Doxyfile ├── .gitmodules ├── .gitignore ├── .travis-setup-linux.sh ├── .editorconfig ├── CMakeLists.txt ├── include ├── sg14 │ ├── fixed_point │ ├── bits │ │ ├── config.h │ │ ├── type_traits.h │ │ ├── fixed_point_make.h │ │ ├── limits.h │ │ ├── fixed_point_common_type.h │ │ ├── fixed_point_named.h │ │ ├── common.h │ │ └── fixed_point_math.h │ └── auxiliary │ │ ├── boost.simd.h │ │ ├── multiprecision.h │ │ ├── boost.multiprecision.h │ │ ├── elastic_fixed_point.h │ │ ├── numeric.h │ │ └── precise_integer.h └── CMakeLists.txt ├── .appveyor.yml ├── LICENSE_1_0.txt ├── .travis.yml └── README.md /src/single_header/footer.h: -------------------------------------------------------------------------------- 1 | 2 | #endif // SG14_FIXED_POINT_SINGLE_HEADER) 3 | -------------------------------------------------------------------------------- /src/single_header/single_header.cpp: -------------------------------------------------------------------------------- 1 | #include "standard_includes.h" 2 | single_header_delimiter 3 | #include "fixed_point_includes.h" 4 | -------------------------------------------------------------------------------- /doc/presentations/2016-09-19_CppCon2016.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnmcfarlane/fixed_point/HEAD/doc/presentations/2016-09-19_CppCon2016.pdf -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "doc/gh-pages"] 2 | path = doc/gh-pages 3 | url = https://github.com/johnmcfarlane/fixed_point.git 4 | branch = gh-pages 5 | -------------------------------------------------------------------------------- /doc/presentations/2016-06-08_ACCU_Silicon_Valley.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnmcfarlane/fixed_point/HEAD/doc/presentations/2016-06-08_ACCU_Silicon_Valley.pdf -------------------------------------------------------------------------------- /src/single_header/standard_includes.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # python 2 | *.pyc 3 | 4 | # VS 5 | *.cache 6 | *.exe 7 | *.ilk 8 | *.ipch 9 | *.log 10 | *.obj 11 | *.pdb 12 | *.sln 13 | *.suo 14 | *.tlog 15 | *.VC.db 16 | *.VC.VC.opendb 17 | *.vcxproj 18 | *.vcxproj.filters 19 | -------------------------------------------------------------------------------- /doc/presentations/2017-05-16_CppNow2017/Composite Arithmetic Types Are _ the + of Their Parts.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnmcfarlane/fixed_point/HEAD/doc/presentations/2017-05-16_CppNow2017/Composite Arithmetic Types Are _ the + of Their Parts.pdf -------------------------------------------------------------------------------- /src/single_header/fixed_point_includes.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | -------------------------------------------------------------------------------- /src/test/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | int main(int argc, char** argv) 10 | { 11 | ::testing::InitGoogleTest(&argc, argv); 12 | return RUN_ALL_TESTS(); 13 | } 14 | -------------------------------------------------------------------------------- /src/test/fixed_point_math_Q15.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright Timo Alho 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #define FPTESTFORMAT exp2on16Q15 8 | #define FPTESTEXP (-15) 9 | 10 | #include "fixed_point_math_common.h" 11 | 12 | #undef FPTESTEXP 13 | #undef FPTESTFORMAT 14 | 15 | 16 | -------------------------------------------------------------------------------- /.travis-setup-linux.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Before install 4 | sudo add-apt-repository -y ppa:kalakris/cmake 5 | sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 6 | sudo apt-get update -qq 7 | 8 | # Install 9 | sudo apt-get install -qq cmake 10 | cmake --version 11 | 12 | # Ubuntu 14.04 defaults to Boost v54 which doesn't work without RTTI 13 | if [[ "$EXCEPTIONS" == "ON" ]]; then 14 | sudo apt-get install -qq libboost-dev 15 | fi 16 | -------------------------------------------------------------------------------- /src/test/fixed_point_math_Q0.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright Timo Alho 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | 8 | 9 | #define FPTESTFORMAT exp2o31Q0 10 | #define FPTESTEXP (-0) 11 | 12 | #include "fixed_point_math_common.h" 13 | 14 | 15 | #undef FPTESTEXP 16 | #undef FPTESTFORMAT 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/test/fixed_point_math_Q1.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright Timo Alho 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | 8 | 9 | #define FPTESTFORMAT exp2o30Q1 10 | #define FPTESTEXP (-1) 11 | 12 | #include "fixed_point_math_common.h" 13 | 14 | 15 | #undef FPTESTEXP 16 | #undef FPTESTFORMAT 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/test/fixed_point_math_Q31.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright Timo Alho 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | 8 | 9 | #define FPTESTFORMAT exp2on0Q31 10 | #define FPTESTEXP (-31) 11 | 12 | #include "fixed_point_math_common.h" 13 | 14 | 15 | #undef FPTESTEXP 16 | #undef FPTESTFORMAT 17 | 18 | 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.{cpp,h}] 14 | indent_size = 4 15 | 16 | [{CMakeLists.txt,*.cmake}] 17 | indent_size = 4 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | 22 | [*.py] 23 | indent_size = 4 24 | 25 | [appveyor.yml] 26 | indent_size = 2 27 | -------------------------------------------------------------------------------- /src/test/fixed_point_math.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright Timo Alho 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | //Fails due to cast being out-of-range: 13 | #include "fixed_point_math_Q0.cpp" 14 | #include "fixed_point_math_Q1.cpp" 15 | #include "fixed_point_math_Q15.cpp" 16 | #include "fixed_point_math_Q31.cpp" 17 | 18 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(fixed_point) 2 | 3 | # CMake 2.8.11 is required by Google Benchmarks 4 | cmake_minimum_required(VERSION 2.8.11) 5 | 6 | include("include/CMakeLists.txt") 7 | 8 | # runs a suite of compile-time tests using `static_assert` 9 | # and run-time tests using gtest 10 | include("src/test/CMakeLists.txt") 11 | 12 | # performs a selection of benchmark tests using googletest 13 | include("src/benchmark/CMakeLists.txt") 14 | 15 | # generate documentation 16 | include("doc/CMakeLists.txt") 17 | 18 | # generate single-header header 19 | include("src/single_header/CMakeLists.txt") 20 | -------------------------------------------------------------------------------- /src/single_header/header.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | // mechanically retrieved, single-header version of fixed_point library 8 | // https://github.com/johnmcfarlane/fixed_point 9 | 10 | #if ! defined(SG14_FIXED_POINT_SINGLE_HEADER) 11 | #define SG14_FIXED_POINT_SINGLE_HEADER 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | -------------------------------------------------------------------------------- /include/sg14/fixed_point: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief all definitions related to the `sg14::fixed_point` type 9 | 10 | #if !defined(SG14_FIXED_POINT_H) 11 | #define SG14_FIXED_POINT_H 1 12 | 13 | #include "bits/fixed_point_type.h" 14 | #include "bits/fixed_point_make.h" 15 | #include "bits/fixed_point_named.h" 16 | #include "bits/fixed_point_common_type.h" 17 | #include "bits/fixed_point_operators.h" 18 | #include "bits/fixed_point_extras.h" 19 | 20 | #endif // SG14_FIXED_POINT_H 21 | -------------------------------------------------------------------------------- /src/test/fixed_point_built_in.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #include 10 | 11 | #define TEST_NATIVE_OVERFLOW 12 | #define TEST_LABEL built_in_ 13 | 14 | //////////////////////////////////////////////////////////////////////////////// 15 | // integer types used as fixed_point Rep type 16 | 17 | using test_int = int; 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | // perform fixed_point tests with this type of fixed_point specialization 21 | 22 | #include "fixed_point_common.h" 23 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.1.{build} 2 | image: Visual Studio 2017 3 | 4 | environment: 5 | fast_finish: true # set this flag to immediately finish build once one of the jobs fails. 6 | matrix: 7 | - platform: x86 8 | configuration: Release 9 | cmake_generator: "Visual Studio 15 2017" 10 | msbuild_property: win32 11 | - platform: x64 12 | configuration: Release 13 | cmake_generator: "Visual Studio 15 2017 Win64" 14 | msbuild_property: x64 15 | 16 | build_script: 17 | - cmd: >- 18 | cmake -G "%cmake_generator%" . 19 | 20 | MSBuild.exe /maxcpucount /property:Configuration=Release /property:Platform=%msbuild_property% fixed_point.sln 21 | test_script: 22 | - cmd: >- 23 | Release\fp_test.exe 24 | 25 | Release\fp_benchmark.exe 26 | shallow_clone: true 27 | -------------------------------------------------------------------------------- /src/test/common.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief file containing tests of the `sg14/bits/common.h` definitions 9 | 10 | #include 11 | 12 | #include 13 | 14 | #include 15 | 16 | namespace { 17 | using sg14::_impl::identical; 18 | 19 | namespace test_min { 20 | using sg14::_impl::min; 21 | static_assert(identical(min(-4, 4), -4), "sg14::_impl::min test failed"); 22 | } 23 | 24 | namespace test_max { 25 | using sg14::_impl::max; 26 | static_assert(identical(max(-4, 4), 4), "sg14::_impl::max test failed"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/number_test.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include "number_test.h" 9 | 10 | template struct number_test; 11 | 12 | template struct number_test; 13 | template struct number_test; 14 | 15 | template struct number_test; 16 | template struct number_test; 17 | 18 | template struct number_test; 19 | template struct number_test; 20 | 21 | template struct number_test; 22 | template struct number_test; 23 | 24 | #if defined(SG14_INT128) 25 | template struct number_test; 26 | template struct number_test; 27 | #endif 28 | -------------------------------------------------------------------------------- /src/common/sample_functions.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #include 10 | 11 | template 12 | constexpr FP magnitude_squared(const FP & x, const FP & y, const FP & z) 13 | { 14 | return static_cast(x * x + y * y + z * z); 15 | } 16 | 17 | template 18 | bool circle_intersect_generic(Real x1, Real y1, Real r1, Real x2, Real y2, Real r2) 19 | { 20 | auto x_diff = x2 - x1; 21 | auto y_diff = y2 - y1; 22 | auto distance_squared = x_diff * x_diff + y_diff * y_diff; 23 | 24 | auto touch_distance = r1 + r2; 25 | auto touch_distance_squared = touch_distance * touch_distance; 26 | 27 | return distance_squared <= touch_distance_squared; 28 | } 29 | -------------------------------------------------------------------------------- /src/test/fixed_point_native_integer.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | #define TEST_NATIVE_OVERFLOW 11 | #define TEST_LABEL native_integer_ 12 | 13 | #if defined(_MSC_VER) 14 | #define TEST_IGNORE_MSVC_INTERNAL_ERRORS_NATIVE 15 | #define TEST_IGNORE_MSVC_INTERNAL_ERRORS 16 | #endif 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | // integer types used as fixed_point Rep type 20 | 21 | using test_int = sg14::safe_integer; 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | // perform fixed_point tests with this type of fixed_point specialization 25 | 26 | #include "fixed_point_common.h" 27 | -------------------------------------------------------------------------------- /src/single_header/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | if (POLICY CMP0054) 3 | cmake_policy(SET CMP0054 NEW) 4 | endif () 5 | 6 | if (${CMAKE_CXX_COMPILER_ID} STREQUAL Clang OR ${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) 7 | # add a target to generate preprocessor output 8 | string(REPLACE " " ";" COMMON_CXX_FLAGS_LIST ${COMMON_CXX_FLAGS}) 9 | set(SINGLE_HEADER_OUTPUT ${CMAKE_CURRENT_LIST_DIR}/fixed_point.h) 10 | set(FLAGS "-E" "-I${CMAKE_CURRENT_LIST_DIR}/../../include" "-std=c++11" "-DSG14_DISABLE_GCC_BUILTINS") 11 | add_custom_target(single_header 12 | ALL 13 | cat ${CMAKE_CURRENT_LIST_DIR}/header.h > ${SINGLE_HEADER_OUTPUT} 14 | COMMAND ${CMAKE_CXX_COMPILER} ${FLAGS} ${CMAKE_CURRENT_LIST_DIR}/single_header.cpp | grep "^[^#]" | grep --after-context=1000000 single_header_delimiter | tail -n +2 >> ${SINGLE_HEADER_OUTPUT} 15 | COMMAND cat ${CMAKE_CURRENT_LIST_DIR}/footer.h >> ${SINGLE_HEADER_OUTPUT} 16 | ) 17 | endif () 18 | -------------------------------------------------------------------------------- /src/test/fixed_point_saturated_integer.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include "sg14/auxiliary/safe_integer.h" 8 | #include 9 | 10 | #define TEST_SATURATED_OVERFLOW 11 | #define TEST_LABEL saturated_integer_ 12 | 13 | #if defined(_MSC_VER) 14 | #define TEST_IGNORE_MSVC_INTERNAL_ERRORS_SATURATED 15 | #define TEST_IGNORE_MSVC_INTERNAL_ERRORS 16 | #endif 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | // integer types used as fixed_point Rep type 20 | 21 | using test_int = sg14::safe_integer; 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | // perform fixed_point tests with this type of fixed_point specialization 25 | 26 | #include "fixed_point_common.h" 27 | -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.11) 2 | 3 | # add a target to generate API documentation with Doxygen 4 | find_package(Doxygen) 5 | if (DOXYGEN_FOUND) 6 | add_custom_target(doc ALL 7 | ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/Doxyfile 8 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 9 | COMMENT "Generating API documentation with Doxygen" VERBATIM) 10 | endif (DOXYGEN_FOUND) 11 | 12 | # add a target to generate HTML files from commonmark files 13 | find_program(PANDOC_EXECUTABLE pandoc) 14 | if (PANDOC_EXECUTABLE) 15 | set(HTML_DIR ${CMAKE_CURRENT_LIST_DIR}/gh-pages/papers) 16 | foreach(PAPER p0381 p0381r1 p0381r0 p0037 p0037r3 p0037r2 p0037r1 p0037r0 p0554 p0675) 17 | set(HTML_FILE ${HTML_DIR}/${PAPER}.html) 18 | set(COMMONMARK_FILE ${CMAKE_CURRENT_LIST_DIR}/${PAPER}.md) 19 | add_custom_target( 20 | "commonmark_${PAPER}" ALL 21 | COMMAND mkdir -p ${HTML_DIR} 22 | COMMAND ${PANDOC_EXECUTABLE} ${COMMONMARK_FILE} > ${HTML_FILE} 23 | DEPENDS ${COMMONMARK_FILE} 24 | ) 25 | endforeach(PAPER) 26 | endif (PANDOC_EXECUTABLE) 27 | -------------------------------------------------------------------------------- /src/test/readme.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | // tests the examples from README.md 8 | 9 | #include 10 | 11 | // 1. Add the [*include*](include) directory to your list of system headers. 12 | 13 | // 2. Essential definitions are in, [*fixed_point*](include/sg14/fixed_point): 14 | #include 15 | 16 | // 5. All definitions are in the `sg14` namespace: 17 | using namespace sg14; 18 | 19 | TEST(readme, instance) 20 | { 21 | std::stringstream std_cout; 22 | 23 | // 6. Use the `fixed_point` type to define variables. 24 | fixed_point pi(3.1415926535); 25 | std_cout << "pi=" << std::setprecision(10) << pi; 26 | 27 | ASSERT_EQ(std_cout.str(), "pi=3.141592652"); 28 | 29 | // 7. Alternatively, use `make_fixed` and `make_ufixed` to instantiate signed and unsigned variables 30 | // with exactly the number of integer and fractional digits you require: 31 | static_assert(std::is_same>::value, "error in README.md example code"); 32 | } 33 | -------------------------------------------------------------------------------- /src/test/glm.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #if defined(_MSC_VER) 8 | #pragma warning(push) 9 | #pragma warning(disable: 4201) 10 | #include 11 | #pragma warning(pop) 12 | #else 13 | #include 14 | #endif 15 | 16 | #include 17 | 18 | #include 19 | 20 | namespace { 21 | TEST(glm, char_multiply) { 22 | auto lhs = glm::tvec4{7}; 23 | auto rhs = glm::tvec4{5}; 24 | 25 | auto expected = glm::tvec4{35}; 26 | auto product = lhs*rhs; 27 | 28 | ASSERT_TRUE(sg14::_impl::identical(expected, product)); 29 | } 30 | 31 | TEST(glm, fp_char_multiply) { 32 | using op_fp = sg14::fixed_point; 33 | auto lhs = glm::tvec4{op_fp{7.5}}; 34 | auto rhs = glm::tvec4{op_fp{5.25}}; 35 | 36 | using result_fp = sg14::fixed_point; 37 | auto expected = glm::tvec4{result_fp{39.375}}; 38 | auto product = lhs*rhs; 39 | 40 | ASSERT_TRUE(sg14::_impl::identical(expected, product)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/benchmark/report.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import csv 4 | import itertools 5 | import sys 6 | 7 | help_text = "please provide CSV-formatted google/benchmark output" 8 | 9 | def sum_from_rows(rows): 10 | return ["total"] + [str(sum(float(cell) for cell in column)) for 11 | column in itertools.islice(zip(*rows), 1, None)] 12 | 13 | def report_from_table(table): 14 | return table + [sum_from_rows(table[1:])] 15 | 16 | def table_from_benchmarks(table): 17 | def filter_row(row): 18 | return [str(cell[0]) for cell in zip(row, table[0]) if cell[1] 19 | in ("name","iterations","real_time","cpu_time")] 20 | 21 | return [filter_row(row) for row in table] 22 | 23 | def report_from_benchmarks(table): 24 | return report_from_table(table_from_benchmarks(table)) 25 | 26 | def benchmarks_from_buffer(buffer): 27 | return list(csv.reader(buffer, delimiter=',', quotechar='"')) 28 | 29 | def report_from_file(filename): 30 | with open(filename) as file: 31 | return report_from_benchmarks( 32 | benchmarks_from_buffer(file)) 33 | 34 | def csv_from_report(table): 35 | return '\n'.join([','.join(row) for row in table]) 36 | 37 | if __name__ == "__main__": 38 | print(csv_from_report(report_from_file(sys.argv[1])) 39 | if len(sys.argv) == 2 else help_text) 40 | -------------------------------------------------------------------------------- /src/test/zero_cost_free_functions.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief Demonstration of selectively applying functions to numbers. 9 | 10 | #include 11 | #include 12 | 13 | using namespace sg14; 14 | 15 | // precise safe integer 16 | template< 17 | class Rep = int, 18 | class RoundingTag = closest_rounding_tag, 19 | class OverflowTag = throwing_overflow_tag> 20 | using precise_safe_integer = safe_integer, OverflowTag>; 21 | 22 | static_assert(identical( 23 | precise_safe_integer<>{2} * precise_safe_integer<>{3}, 24 | precise_safe_integer<>{6}), ""); 25 | 26 | static_assert(identical( 27 | multiply(saturated_overflow, precise_safe_integer<>{INT_MAX}, precise_safe_integer<>{INT_MAX}), 28 | precise_safe_integer<>{INT_MAX}), ""); 29 | 30 | int bare_saturate(int a, int b) { 31 | return multiply(saturated_overflow, a, b); 32 | } 33 | 34 | precise_safe_integer<> psi_saturate(precise_safe_integer<> a, precise_safe_integer<> b) { 35 | return multiply(saturated_overflow, a, b); 36 | } 37 | -------------------------------------------------------------------------------- /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(fixed_point) 2 | 3 | # CMake 2.8.11 is required by Google Benchmarks 4 | cmake_minimum_required(VERSION 2.8.11) 5 | 6 | add_library(fixed_point INTERFACE) 7 | target_sources(fixed_point INTERFACE 8 | include/sg14/auxiliary/boost.simd.h 9 | include/sg14/auxiliary/boost.multiprecision.h 10 | include/sg14/auxiliary/elastic_integer.h 11 | include/sg14/auxiliary/elastic_fixed_point.h 12 | include/sg14/auxiliary/numeric.h 13 | include/sg14/auxiliary/overflow.h 14 | include/sg14/auxiliary/safe_integer.h 15 | include/sg14/bits/fixed_point_math.h 16 | include/sg14/bits/fixed_point_operators.h 17 | include/sg14/bits/fixed_point_make.h 18 | include/sg14/bits/fixed_point_arithmetic.h 19 | include/sg14/bits/fixed_point_type.h 20 | include/sg14/bits/fixed_point_common_type.h 21 | include/sg14/bits/fixed_point_named.h 22 | include/sg14/bits/fixed_point_extras.h 23 | include/sg14/bits/common.h 24 | include/sg14/bits/config.h 25 | include/sg14/cstdint 26 | include/sg14/fixed_point 27 | include/sg14/limits 28 | include/sg14/type_traits 29 | ) 30 | 31 | include_directories(SYSTEM ${CMAKE_CURRENT_LIST_DIR}) 32 | install( 33 | DIRECTORY include/sg14 34 | DESTINATION include 35 | ) 36 | -------------------------------------------------------------------------------- /src/test/precise_fixed_point.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | namespace { 11 | using sg14::_impl::identical; 12 | using sg14::precise_integer; 13 | 14 | template 15 | using precise_fixed_point = sg14::fixed_point, Exponent>; 16 | 17 | namespace test_numeric_limits { 18 | static_assert(std::numeric_limits>::is_specialized, "std::numeric_limits> test failed"); 19 | static_assert(std::numeric_limits>::is_integer, "std::numeric_limits> test failed"); 20 | } 21 | 22 | namespace test_ctor { 23 | using sg14::_impl::from_rep; 24 | 25 | static_assert(identical( 26 | precise_fixed_point<>(-8).data(), 27 | precise_integer<>(-8)), "precise_fixed_point ctor test failed"); 28 | static_assert(precise_fixed_point<>(0) == from_rep>(0), "precise_fixed_point ctor test failed"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /include/sg14/bits/config.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #ifndef SG14_CONFIG_H 8 | #define SG14_CONFIG_H 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | // SG14_INT128_ENABLED macro definition 12 | 13 | #if defined(SG14_INT128_ENABLED) 14 | #error SG14_INT128_ENABLED already defined 15 | #endif 16 | 17 | #if defined(SG14_USE_INT128) 18 | 19 | // GCC/Clang 64-bit builds support 128-bit integer through __int128 type 20 | #if defined(__SIZEOF_INT128__) 21 | #define SG14_INT128_ENABLED 22 | using SG14_INT128 = __int128; 23 | using SG14_UINT128 = unsigned __int128; 24 | #endif 25 | 26 | #endif // defined(SG14_USE_INT128) 27 | 28 | //////////////////////////////////////////////////////////////////////////////// 29 | // SG14_EXCEPTIONS_ENABLED macro definition 30 | 31 | #if defined(SG14_EXCEPTIONS_ENABLED) 32 | #error SG14_EXCEPTIONS_ENABLED already defined 33 | #endif 34 | 35 | #if defined(_MSC_VER) 36 | #if defined(_CPPUNWIND) 37 | #define SG14_EXCEPTIONS_ENABLED 38 | #endif 39 | #elif defined(__clang__) || defined(__GNUG__) 40 | #if defined(__EXCEPTIONS) 41 | #define SG14_EXCEPTIONS_ENABLED 42 | #endif 43 | #else 44 | #define SG14_EXCEPTIONS_ENABLED 45 | #endif 46 | 47 | #endif // SG14_CONFIG_H 48 | -------------------------------------------------------------------------------- /include/sg14/auxiliary/boost.simd.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief definitions and specializations that adapt Boost.SIMD for use with @ref sg14::fixed_point 9 | 10 | #if !defined(SG14_BOOST_SIMD_H) 11 | #define SG14_BOOST_SIMD_H 1 12 | 13 | #include 14 | 15 | #include 16 | 17 | namespace sg14 { 18 | template 19 | ::std::ostream& operator<<(::std::ostream& out, const fixed_point, Exponent>& fp) 20 | { 21 | return out << static_cast>(fp); 22 | } 23 | 24 | template 25 | struct set_digits, Digits> { 26 | using type = boost::simd::pack, N>; 27 | }; 28 | 29 | template 30 | struct make_signed> { 31 | using type = boost::simd::pack, N>; 32 | }; 33 | 34 | template 35 | struct make_unsigned> { 36 | using type = boost::simd::pack, N>; 37 | }; 38 | } 39 | 40 | namespace std { 41 | template 42 | struct numeric_limits> : numeric_limits {}; 43 | } 44 | 45 | #endif // SG14_BOOST_SIMD_H 46 | -------------------------------------------------------------------------------- /include/sg14/bits/type_traits.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief trait definitions related to the `sg14::fixed_point` type 9 | 10 | #if !defined(SG14_TYPE_TRAITS_H) 11 | #define SG14_TYPE_TRAITS_H 1 12 | 13 | #include 14 | 15 | /// study group 14 of the C++ working group 16 | namespace sg14 { 17 | namespace _impl { 18 | //////////////////////////////////////////////////////////////////////////////// 19 | // sg14::_impl::common_type_t 20 | 21 | // pre-C++14 common_type_t 22 | template 23 | using common_type_t = typename std::common_type::type; 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | // sg14::_impl::enable_if_t 27 | 28 | // pre-C++14 enable_if_t 29 | template 30 | using enable_if_t = typename std::enable_if::type; 31 | 32 | //////////////////////////////////////////////////////////////////////////////// 33 | // sg14::_impl::identical - compiles iff same type; returns true iff equal 34 | 35 | template 36 | constexpr bool identical(const A& a, const B& b) 37 | { 38 | static_assert(std::is_same::value, "different types"); 39 | return a==b; 40 | } 41 | } 42 | } 43 | 44 | #endif // SG14_TYPE_TRAITS_H 45 | -------------------------------------------------------------------------------- /src/test/fixed_point_throwing_integer.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | #define TEST_THROWING_OVERFLOW 11 | #define TEST_LABEL throwing_integer_ 12 | 13 | #if defined(_MSC_VER) 14 | #define TEST_IGNORE_MSVC_INTERNAL_ERRORS_THROWING 15 | #define TEST_IGNORE_MSVC_INTERNAL_ERRORS 16 | #endif 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | // integer types used as fixed_point Rep type 20 | 21 | using test_int = sg14::safe_integer<>; 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | // perform fixed_point tests with this type of fixed_point specialization 25 | 26 | #include "fixed_point_common.h" 27 | 28 | //////////////////////////////////////////////////////////////////////////////// 29 | // throwing_integer-specific exceptions tests 30 | 31 | 32 | #if defined(TEST_THROWING_OVERFLOW) && defined(SG14_EXCEPTIONS_ENABLED) 33 | 34 | TEST(TOKENPASTE2(TEST_LABEL, overflow_exception), shift_right) 35 | { 36 | auto shift_left_fn = shift_left<-8, uint16, uint8>; 37 | ASSERT_THROW(shift_left_fn((uint8) 0x1234), std::overflow_error); 38 | } 39 | 40 | TEST(TOKENPASTE2(TEST_LABEL, overflow_exception), shift_left) 41 | { 42 | auto shift_left_fn = shift_left<-8, uint16, uint8>; 43 | ASSERT_THROW(shift_left_fn((uint8) 0x1234), std::overflow_error); 44 | } 45 | 46 | TEST(TOKENPASTE2(TEST_LABEL, overflow_exception), assignment) 47 | { 48 | using fp_type = fixed_point; 49 | ASSERT_THROW(fp_type(1), std::overflow_error); 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/test/p0554.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | namespace { 10 | using sg14::fixed_point; 11 | using sg14::_impl::identical; 12 | 13 | namespace bare_metal { 14 | // sample 1 - bare-metal fixed-point arithmetic 15 | constexpr auto a = (int8_t)(7.f * 8); // the value 7 stored in a byte with 3 fractional bits 16 | constexpr auto b = (int8_t)(3.125f * 16); // the value 3.125 stored in a byte with 4 fractional bits 17 | constexpr auto c = a * b; // the value 21.875 stored in an `int` with 7 fractional bits 18 | constexpr auto d = (float)c / 128; // 21.875f 19 | static_assert(identical(d, 21.875f), "position_paper test failed"); 20 | } 21 | 22 | namespace type_safe { 23 | // sample 2 - type-safe fixed-point arithmetic 24 | constexpr auto a = fixed_point(7.f); // the value 7 stored in a byte with 3 fractional bits 25 | constexpr auto b = fixed_point(3.125f); // the value 3.125 stored in a byte with 4 fractional bits 26 | constexpr auto c = a * b; // the value 21.875 stored in an `int` with 7 fractional bits 27 | constexpr auto d = (float)c; // 21.875f 28 | static_assert(identical(d, 21.875f), "position_paper test failed"); 29 | } 30 | 31 | namespace division { 32 | using sg14::elastic_fixed_point; 33 | constexpr elastic_fixed_point<1, 6> numerator = 0.5, denominator = 1.0; 34 | constexpr auto quotient = numerator / denominator; 35 | static_assert(identical(quotient, elastic_fixed_point<7, 7>{.5}), "position_paper test failed"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/common/fixed_point.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {_r+0} 5 | 6 | 7 | 17 | 18 | 19 | {(long double)_r} u{integer_digits}:{fractional_digits} 20 | {(long double)_r} s{integer_digits}:{fractional_digits} 21 | {(long double)_r/(1<<-exponent)} u{integer_digits}:{fractional_digits} 22 | {(long double)_r/(1<<-exponent)} s{integer_digits}:{fractional_digits} 23 | {(long double)_r*(1<<+exponent)} u{integer_digits}:{fractional_digits} 24 | {(long double)_r*(1<<+exponent)} s{integer_digits}:{fractional_digits} 25 | 26 | _r 27 | exponent 28 | digits 29 | integer_digits 30 | fractional_digits 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.4) 2 | if (POLICY CMP0054) 3 | cmake_policy(SET CMP0054 NEW) 4 | endif() 5 | 6 | include("${CMAKE_CURRENT_LIST_DIR}/../common/common.cmake") 7 | 8 | ###################################################################### 9 | # fp_benchmark target 10 | 11 | add_executable( 12 | fp_benchmark 13 | ${CMAKE_CURRENT_LIST_DIR}/benchmark.cpp 14 | ) 15 | set_target_properties( 16 | fp_benchmark 17 | PROPERTIES COMPILE_FLAGS "${COMMON_CXX_FLAGS}" 18 | ) 19 | 20 | ###################################################################### 21 | # add external project, google/benchmark 22 | 23 | ExternalProject_Add( 24 | google_benchmark 25 | URL "https://github.com/google/benchmark/archive/v1.1.0.tar.gz" 26 | URL_MD5 "66b2a23076cf70739525be0092fc3ae3" 27 | CMAKE_ARGS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:STRING= 28 | ) 29 | ExternalProject_Get_Property(google_benchmark source_dir) 30 | include_directories(SYSTEM ${source_dir}/include) 31 | 32 | # not sure why this isn't taken care of in google_benchmark/src/CMakeLists.txt 33 | if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 34 | target_link_libraries(fp_benchmark Shlwapi) 35 | endif() 36 | 37 | find_package(Threads REQUIRED) 38 | 39 | ###################################################################### 40 | # hitch google_benchmark to test 41 | 42 | add_dependencies(fp_benchmark google_benchmark) 43 | ExternalProject_Get_Property(google_benchmark binary_dir) 44 | 45 | if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) 46 | target_link_libraries( 47 | fp_benchmark 48 | debug ${binary_dir}/src/Debug/${CMAKE_FIND_LIBRARY_PREFIXES}benchmark${CMAKE_FIND_LIBRARY_SUFFIXES} 49 | optimized ${binary_dir}/src/Release/${CMAKE_FIND_LIBRARY_PREFIXES}benchmark${CMAKE_FIND_LIBRARY_SUFFIXES} 50 | ) 51 | else () 52 | target_link_libraries( 53 | fp_benchmark 54 | general ${binary_dir}/src/${CMAKE_FIND_LIBRARY_PREFIXES}benchmark.a 55 | general pthread 56 | ) 57 | endif () 58 | -------------------------------------------------------------------------------- /src/test/zero_cost_square.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief Signed 15:16 Fixed-Point Square Function Using sg14::elastic_fixed_point 9 | 10 | // Here's how to use the fixed_point library on Godbolt.org. 11 | // Normally, you'd just add `#include `. 12 | #include 13 | using namespace sg14; 14 | 15 | // square a nunber using 15:16 fixed-point arithmetic 16 | // without using a fixed-point library 17 | float square_int(float input) { 18 | // user must scale values by the correct amount 19 | auto fixed = static_cast(input * 65536.f); 20 | 21 | // user must remember to widen the result to avoid overflow 22 | auto prod = int64_t{fixed} * fixed; 23 | 24 | // user must remember that the scale also was squared 25 | return prod / 4294967296.f; 26 | } 27 | 28 | // the same function using sg14::elastic_integer 29 | float square_elastic_integer(float input) { 30 | auto fixed = elastic_integer<31>{input * 65536.f}; 31 | 32 | // elastic_integer automatically widens the result 33 | auto prod = fixed * fixed; 34 | 35 | // but the user must still do all the scaling themselves 36 | return static_cast(prod) / 4294967296.f; 37 | } 38 | 39 | // the same function using sg14::fixed_point 40 | float square_fixed_point(float input) { 41 | // fixed_point handles scaling 42 | auto fixed = fixed_point{input}; 43 | 44 | // but it uses int under the hood; user must still widen 45 | auto prod = fixed_point{fixed} * fixed; 46 | 47 | return static_cast(prod); 48 | } 49 | 50 | // finally, the composition of fixed_point and elastic_integer 51 | float square_elastic(float input) { 52 | // alias to fixed_point, -16> 53 | auto fixed = elastic_fixed_point<15, 16>{input}; 54 | 55 | // concise, safe and zero-cost! 56 | auto prod = fixed * fixed; 57 | 58 | return static_cast(prod); 59 | } 60 | -------------------------------------------------------------------------------- /include/sg14/bits/fixed_point_make.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief definitions of `sg14::make_fixed` and `sg14::make_ufixed` 9 | 10 | #if !defined(SG14_MAKE_FIXED_H) 11 | #define SG14_MAKE_FIXED_H 1 12 | 13 | #include "fixed_point_type.h" 14 | 15 | #include 16 | 17 | /// study group 14 of the C++ working group 18 | namespace sg14 { 19 | 20 | /// \brief specializes \ref fixed_point with the given number of integer and fractional digits 21 | /// \headerfile sg14/fixed_point 22 | /// 23 | /// \tparam IntegerDigits specifies minimum value of @ref fixed_point::integer_digits 24 | /// \tparam FractionalDigits specifies the exact value of @ref fixed_point::fractional_digits 25 | /// \tparam Narrowest hints at the type of @ref fixed_point::rep 26 | /// 27 | /// \remarks The signage of \a Narrowest specifies signage of the resultant fixed-point type. 28 | /// \remarks Typical choices for \a Narrowest, `signed` and `unsigned`, 29 | /// result in a type that uses built-in integers for \a fixed_point::rep. 30 | /// \remarks Resultant type is signed by default. 31 | /// 32 | /// \par Example: 33 | /// 34 | /// To generate a fixed-point type with a sign bit, 8 fractional bits and at least 7 integer bits: 35 | /// \snippet snippets.cpp use make_fixed 36 | /// 37 | /// \sa make_ufixed 38 | template 39 | using make_fixed = fixed_point< 40 | set_digits_t, 41 | -FractionalDigits>; 42 | 43 | /// \brief specializes \ref fixed_point with the given number of integer and fractional digits; produces an unsigned type 44 | /// \headerfile sg14/fixed_point 45 | /// 46 | /// \sa make_fixed 47 | template 48 | using make_ufixed = make_fixed< 49 | IntegerDigits, 50 | FractionalDigits, 51 | typename make_unsigned::type>; 52 | } 53 | 54 | #endif // SG14_MAKE_FIXED_H 55 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | 4 | language: cpp 5 | 6 | matrix: 7 | include: 8 | - os: linux 9 | addons: &gcc7 10 | apt: 11 | sources: 12 | - ubuntu-toolchain-r-test 13 | packages: 14 | - g++-7 15 | env: COMPILER=g++-7 BUILD_TYPE=Release STD=17 EXCEPTIONS=ON INT128=ON 16 | 17 | - os: linux 18 | addons: *gcc7 19 | env: COMPILER=g++-7 BUILD_TYPE=Debug STD=11 EXCEPTIONS=OFF INT128=OFF 20 | 21 | - os: linux 22 | addons: &gcc5 23 | apt: 24 | sources: 25 | - ubuntu-toolchain-r-test 26 | packages: 27 | - g++-5 28 | env: COMPILER=g++-5 BUILD_TYPE=Release STD=14 EXCEPTIONS=ON INT128=ON 29 | 30 | - os: linux 31 | addons: *gcc5 32 | env: COMPILER=g++-5 BUILD_TYPE=Release STD=11 EXCEPTIONS=OFF INT128=OFF 33 | 34 | - os: linux 35 | addons: *gcc5 36 | env: COMPILER=g++-5 BUILD_TYPE=Debug STD=14 EXCEPTIONS=ON INT128=ON 37 | 38 | - os: linux 39 | addons: *gcc5 40 | env: COMPILER=g++-5 BUILD_TYPE=Debug STD=11 EXCEPTIONS=OFF INT128=OFF 41 | 42 | - os: linux 43 | compiler: clang 44 | env: COMPILER=clang++ BUILD_TYPE=Release STD=11 EXCEPTIONS=OFF INT128=OFF 45 | 46 | - os: linux 47 | compiler: clang 48 | env: COMPILER=clang++ BUILD_TYPE=Debug STD=11 EXCEPTIONS=OFF INT128=OFF 49 | 50 | - os: osx 51 | compiler: g++ 52 | env: COMPILER=c++ BUILD_TYPE=Release STD=14 EXCEPTIONS=ON INT128=ON 53 | 54 | - os: osx 55 | compiler: g++ 56 | env: COMPILER=c++ BUILD_TYPE=Debug STD=14 EXCEPTIONS=ON INT128=ON 57 | 58 | - os: osx 59 | compiler: clang 60 | env: COMPILER=c++ BUILD_TYPE=Release STD=14 EXCEPTIONS=ON INT128=ON 61 | 62 | - os: osx 63 | compiler: clang 64 | env: COMPILER=c++ BUILD_TYPE=Debug STD=14 EXCEPTIONS=ON INT128=ON 65 | 66 | before_install: 67 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then source .travis-setup-linux.sh ; fi 68 | 69 | script: 70 | - export CXX="${COMPILER}" 71 | - cmake -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" -DSTD="${STD}" -DEXCEPTIONS="${EXCEPTIONS}" -DINT128="${INT128}" 72 | - make 73 | - ./fp_test 74 | - ./fp_benchmark --benchmark_format=csv>result.csv 75 | - ./src/benchmark/report.py result.csv 76 | - ls -l fp_benchmark fp_test 77 | -------------------------------------------------------------------------------- /src/test/precise_safe_elastic_integer.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace sg14 { 12 | // precise safe elastic fixed-point 13 | template< 14 | int IntegerDigits, 15 | class OverflowTag = safe_integer<>::overflow_tag, 16 | class RoundingTag = precise_integer<>::rounding, 17 | class Narrowest = int> 18 | using psei = elastic_integer< 19 | IntegerDigits, 20 | precise_integer< 21 | safe_integer< 22 | Narrowest, 23 | OverflowTag 24 | >, 25 | RoundingTag 26 | > 27 | >; 28 | 29 | template< 30 | class OverflowTag = safe_integer<>::overflow_tag, 31 | class RoundingTag = precise_integer<>::rounding, 32 | class Narrowest = int, 33 | class Input = int> 34 | psei< 35 | std::numeric_limits::digits, 36 | OverflowTag, RoundingTag, 37 | Narrowest> 38 | constexpr make_psei(Input const& input) 39 | { 40 | return input; 41 | } 42 | } 43 | 44 | namespace { 45 | using sg14::make_psei; 46 | using sg14::psei; 47 | using std::is_same; 48 | using sg14::_impl::identical; 49 | 50 | namespace default_parameters { 51 | using sg14::precise_integer; 52 | using sg14::safe_integer; 53 | using sg14::elastic_integer; 54 | 55 | static_assert( 56 | is_same::rep::rep::rep, int>::value, 57 | "sg14::precise_integer parameter default test failed"); 58 | } 59 | 60 | namespace test_multiply { 61 | static_assert(identical(sg14::psei<6>{7}*sg14::psei<13>{321}, sg14::psei<19>{2247}), ""); 62 | } 63 | 64 | namespace test_make_psei { 65 | using namespace sg14::literals; 66 | static_assert(identical(make_psei(std::int16_t{7}), psei<15>{7}), ""); 67 | static_assert(identical(make_psei(7_c), psei<3>{7}), ""); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/precise_elastic_integer.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | namespace sg14 { 11 | // precise elastic integer 12 | template< 13 | int IntegerDigits, 14 | class RoundingTag = precise_integer<>::rounding, 15 | class Narrowest = int> 16 | using precise_elastic_integer = precise_integer< 17 | elastic_integer< 18 | IntegerDigits, 19 | Narrowest>, 20 | RoundingTag>; 21 | 22 | template< 23 | class RoundingTag = precise_integer<>::rounding, 24 | class Narrowest = int, 25 | class Input = int> 26 | precise_elastic_integer< 27 | std::numeric_limits::digits, 28 | RoundingTag, 29 | Narrowest> 30 | constexpr make_precise_elastic(Input const& input) 31 | { 32 | return input; 33 | } 34 | } 35 | 36 | namespace { 37 | using sg14::precise_elastic_integer; 38 | using std::is_same; 39 | using sg14::_impl::identical; 40 | 41 | namespace default_parameters { 42 | using sg14::precise_integer; 43 | using sg14::elastic_integer; 44 | 45 | static_assert( 46 | is_same::rep::rep, int>::value, 47 | "sg14::precise_integer parameter default test failed"); 48 | } 49 | 50 | namespace test_make_precise_elastic { 51 | static_assert(identical(precise_elastic_integer<1>{1}, precise_elastic_integer<1>{1}), ""); 52 | static_assert(identical(sg14::make_precise_elastic(std::int16_t{7}), precise_elastic_integer<15>{7}), ""); 53 | } 54 | 55 | namespace test_multiply { 56 | static_assert(identical(precise_elastic_integer<3>{7}*precise_elastic_integer<4>{10}, precise_elastic_integer<7>{70}), "precise_elastic_integer operator*"); 57 | //static_assert(identical(precise_elastic_integer<3>{3}*.25, precise_elastic_integer<3>{.75}), "precise_elastic_integer operator*"); 58 | } 59 | 60 | namespace test_scale { 61 | static_assert(identical(precise_elastic_integer<3>{7}*precise_elastic_integer<4>{10}, precise_elastic_integer<7>{70}), "precise_elastic_integer operator*"); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/common/common.cmake: -------------------------------------------------------------------------------- 1 | if (POLICY CMP0054) 2 | cmake_policy(SET CMP0054 NEW) 3 | endif() 4 | 5 | include(ExternalProject) 6 | 7 | ###################################################################### 8 | # build flags 9 | 10 | if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) 11 | set(MISC_FLAGS "/W4 /WX /errorReport:prompt /nologo") 12 | 13 | # no tested 14 | set(CPP17_ENABLED_FLAGS "/std:c++latest") 15 | 16 | set(EXCEPTION_ENABLED_FLAGS "/EHsc") 17 | set(EXCEPTION_DISABLED_FLAGS "-DBOOST_NO_EXCEPTIONS -DBOOST_NO_RTTI") 18 | 19 | # 128-bit integers are not supported in MSVC 20 | set(INT128_ENABLED_FLAGS "") 21 | set(INT128_DISABLED_FLAGS "") 22 | 23 | set(PROFILE_ENABLED_FLAGS "/Oy-") 24 | set(PROFILE_DISABLED_FLAGS "") 25 | elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL Clang OR ${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) 26 | set(MISC_FLAGS "-pthread -Wall -Wextra -Werror -Wfatal-errors") 27 | 28 | set(CPP17_ENABLED_FLAGS "-std=c++17") 29 | 30 | set(EXCEPTION_ENABLED_FLAGS "-fexceptions -frtti") 31 | set(EXCEPTION_DISABLED_FLAGS "-DBOOST_NO_EXCEPTIONS -DBOOST_NO_RTTI -fno-exceptions -fno-rtti") 32 | 33 | set(INT128_ENABLED_FLAGS "-DSG14_USE_INT128") 34 | set(INT128_DISABLED_FLAGS "") 35 | 36 | set(PROFILE_ENABLED_FLAGS "-fno-omit-frame-pointer") 37 | set(PROFILE_DISABLED_FLAGS "") 38 | else () 39 | message(FATAL_ERROR "unrecognized compiler: ${CMAKE_CXX_COMPILER_ID}") 40 | endif () 41 | 42 | set(STD 14 CACHE STRING "version of C++ standard: 11, 14 or 17 (experimental)") 43 | if (${STD} STREQUAL "17") 44 | set(STD_FLAGS "${CPP17_ENABLED_FLAGS}") 45 | else () 46 | set(CMAKE_CXX_STANDARD ${STD}) 47 | endif () 48 | 49 | set(EXCEPTIONS ON CACHE BOOL "compile with exceptions enabled") 50 | if (EXCEPTIONS) 51 | set(EXCEPTION_FLAGS "${EXCEPTION_ENABLED_FLAGS}") 52 | else (EXCEPTIONS) 53 | set(EXCEPTION_FLAGS "${EXCEPTION_DISABLED_FLAGS}") 54 | endif (EXCEPTIONS) 55 | 56 | set(INT128 ON CACHE BOOL "compile with support for 128-bit integers (where available)") 57 | if (INT128) 58 | set(INT128_FLAGS "${INT128_ENABLED_FLAGS}") 59 | else (INT128) 60 | set(INT128_FLAGS "${INT128_DISABLED_FLAGS}") 61 | endif (INT128) 62 | 63 | set(PROFILE OFF CACHE BOOL "compile with frame pointer enabled for better profiling information") 64 | if (PROFILE) 65 | set(PROFILE_FLAGS "${PROFILE_ENABLED_FLAGS}") 66 | else (PROFILE) 67 | set(PROFILE_FLAGS "${PROFILE_DISABLED_FLAGS}") 68 | endif (PROFILE) 69 | 70 | set(COMMON_CXX_FLAGS "${MISC_FLAGS} ${STD_FLAGS} ${EXCEPTION_FLAGS} ${INT128_FLAGS} ${PROFILE_FLAGS}") 71 | 72 | include_directories("${CMAKE_CURRENT_LIST_DIR}/.") 73 | 74 | set(BUILD_SHARED_LIBS, ON) 75 | -------------------------------------------------------------------------------- /src/test/utils.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | using sg14::fixed_point; 13 | using sg14::make_fixed; 14 | using sg14::make_ufixed; 15 | 16 | TEST(utils_tests, sin) 17 | { 18 | ASSERT_EQ(sin(fixed_point(0)), 0); 19 | ASSERT_EQ(sin(fixed_point(3.1415926)), 0.f); 20 | ASSERT_EQ(sin(fixed_point(3.1415926/2)), 1); 21 | ASSERT_EQ(sin(fixed_point(3.1415926*7./2.)), -1); 22 | ASSERT_EQ(sin(fixed_point(3.1415926/4)), .707106769f); 23 | ASSERT_EQ(sin(fixed_point(-3.1415926/3)), -.865234375); 24 | } 25 | 26 | TEST(utils_tests, cos) 27 | { 28 | ASSERT_EQ(cos(fixed_point(0)), 1.f); 29 | ASSERT_EQ(cos(fixed_point(3.1415926)), -1); 30 | ASSERT_EQ(cos(fixed_point(3.1415926/2)), 0.L); 31 | ASSERT_EQ(cos(fixed_point(3.1415926*7./2.)), 0.f); 32 | ASSERT_EQ(cos(fixed_point(3.1415926/4)), .707106829f); 33 | ASSERT_EQ(cos(fixed_point(-3.1415926/3)), .5L); 34 | } 35 | 36 | //////////////////////////////////////////////////////////////////////////////// 37 | // sg14::abs 38 | 39 | static_assert(abs(make_fixed<7, 0>(66))==66, "sg14::abs test failed"); 40 | static_assert(abs(make_fixed<7, 0>(-123))==123, "sg14::abs test failed"); 41 | static_assert(abs(make_fixed<63, 0>(9223372036854775807))==9223372036854775807LL, "sg14::abs test failed"); 42 | static_assert(abs(make_fixed<63, 0>(-9223372036854775807))==9223372036854775807LL, "sg14::abs test failed"); 43 | static_assert(abs(make_fixed<7, 8>(-5))==5, "sg14::abs test failed"); 44 | 45 | static_assert(abs(make_ufixed<8, 0>(66))==66, "sg14::abs test failed"); 46 | static_assert(abs(make_ufixed<8, 0>(123))==123, "sg14::abs test failed"); 47 | static_assert(abs(make_ufixed<8, 8>(5))==5, "sg14::abs test failed"); 48 | 49 | //////////////////////////////////////////////////////////////////////////////// 50 | // std specializations for 128-bit integer facilitate certain 64-bit operations 51 | 52 | #if defined(SG14_INT128_ENABLE) 53 | static_assert((make_ufixed<56, 8>(1003006)*make_ufixed<56, 8>(7))==7021042, "sg14::fixed_point test failed"); 54 | static_assert(static_cast((fixed_point(65535)/fixed_point(256)))==255, 55 | "sg14::fixed_point test failed"); 56 | static_assert(sqrt(make_fixed<63, 0>(9223372036854775807))==3037000499LL, "sg14::sqrt test failed"); 57 | #endif 58 | -------------------------------------------------------------------------------- /include/sg14/bits/limits.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief specialization of `std::numeric_limits` for 128-bit integer types 9 | 10 | #if !defined(SG14_LIMITS_H) 11 | #define SG14_LIMITS_H 1 12 | 13 | #include "config.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | // SG14_NUMERIC_LIMITS_128_PROVIDED defined if 20 | // standard library specializes std::numeric_limits for 128-bit integer 21 | #if !defined(__clang__) && defined(__GNUG__) && (__cplusplus <= 201402L) 22 | #define SG14_NUMERIC_LIMITS_128_PROVIDED 23 | #elif defined(SG14_NUMERIC_LIMITS_128_PROVIDED) 24 | #error SG14_NUMERIC_LIMITS_128_PROVIDED already defined 25 | #endif 26 | 27 | #if defined(SG14_INT128_ENABLED) && !defined(SG14_NUMERIC_LIMITS_128_PROVIDED) 28 | 29 | namespace std { 30 | template<> 31 | struct numeric_limits : numeric_limits { 32 | static const int digits = CHAR_BIT*sizeof(SG14_INT128)-1; 33 | static const int digits10 = 38; 34 | 35 | struct _s { 36 | constexpr _s(uint64_t upper, uint64_t lower) : value(lower + (SG14_INT128{upper} << 64)) {} 37 | constexpr operator SG14_INT128() const { return value; } 38 | SG14_INT128 value; 39 | }; 40 | 41 | static constexpr SG14_INT128 min() 42 | { 43 | return _s(0x8000000000000000, 0x0000000000000000); 44 | } 45 | 46 | static constexpr SG14_INT128 max() 47 | { 48 | return _s(0x7fffffffffffffff, 0xffffffffffffffff); 49 | } 50 | 51 | static constexpr SG14_INT128 lowest() 52 | { 53 | return min(); 54 | } 55 | }; 56 | 57 | template<> 58 | struct numeric_limits : numeric_limits { 59 | static const int digits = CHAR_BIT*sizeof(SG14_INT128); 60 | static const int digits10 = 38; 61 | 62 | struct _s { 63 | constexpr _s(uint64_t upper, uint64_t lower) : value(lower + (SG14_UINT128{upper} << 64)) {} 64 | constexpr operator SG14_INT128() const { return value; } 65 | SG14_UINT128 value; 66 | }; 67 | 68 | static constexpr SG14_INT128 min() 69 | { 70 | return 0; 71 | } 72 | 73 | static constexpr SG14_INT128 max() 74 | { 75 | return _s(0xffffffffffffffff, 0xffffffffffffffff); 76 | } 77 | 78 | static constexpr SG14_INT128 lowest() 79 | { 80 | return min(); 81 | } 82 | }; 83 | } 84 | 85 | #endif // SG14_INT128_ENABLED 86 | 87 | #endif // SG14_LIMITS_H 88 | -------------------------------------------------------------------------------- /src/test/precise_safe_elastic_fixed_point.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace sg14 { 13 | // precise safe elastic fixed-point 14 | template< 15 | int IntegerDigits, 16 | int FractionalDigits = 0, 17 | class OverflowTag = safe_integer<>::overflow_tag, 18 | class RoundingTag = precise_integer<>::rounding, 19 | class Narrowest = int> 20 | using psefp = fixed_point< 21 | elastic_integer< 22 | IntegerDigits+FractionalDigits, 23 | precise_integer< 24 | safe_integer< 25 | Narrowest, 26 | OverflowTag 27 | >, 28 | RoundingTag 29 | > 30 | >, 31 | -FractionalDigits 32 | >; 33 | 34 | template< 35 | class OverflowTag = safe_integer<>::overflow_tag, 36 | class RoundingTag = precise_integer<>::rounding, 37 | class Narrowest = int, 38 | class Input = int> 39 | psefp< 40 | std::numeric_limits::digits, 0, 41 | OverflowTag, RoundingTag, 42 | Narrowest> 43 | constexpr make_psefp(Input const& input) 44 | { 45 | return input; 46 | } 47 | 48 | template< 49 | class OverflowTag = safe_integer<>::overflow_tag, 50 | class RoundingTag = precise_integer<>::rounding, 51 | class Narrowest = int, 52 | class Input = int, 53 | class Integral, 54 | Integral Value, 55 | int Digits, 56 | int Exponent> 57 | psefp< 58 | Digits, -Exponent, 59 | OverflowTag, RoundingTag, 60 | Narrowest> 61 | constexpr make_psefp(const const_integer&) 62 | { 63 | return Value; 64 | } 65 | } 66 | 67 | namespace { 68 | using sg14::make_psefp; 69 | using sg14::psefp; 70 | using std::is_same; 71 | using sg14::_impl::identical; 72 | 73 | namespace default_parameters { 74 | using sg14::precise_integer; 75 | using sg14::safe_integer; 76 | using sg14::elastic_integer; 77 | 78 | static_assert( 79 | is_same::rep::rep::rep::rep, int>::value, 80 | "sg14::precise_integer parameter default test failed"); 81 | } 82 | 83 | namespace test_make_psefp { 84 | using namespace sg14::literals; 85 | static_assert(identical(make_psefp(std::int16_t{7}), psefp<15>{7}), ""); 86 | static_assert(identical(make_psefp(444_c), psefp<9, -2>{444}), ""); 87 | } 88 | 89 | namespace test_multiply { 90 | static_assert(identical(sg14::psefp<6>{7}*sg14::psefp<13>{321}, sg14::psefp<19>{2247}), ""); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/test/snippets.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | using namespace sg14; 11 | 12 | // contains snippets of code displayed in Doxygen documentation as examples 13 | namespace { 14 | 15 | namespace define_a_fixed_point_value { 16 | //! [define a fixed_point value] 17 | constexpr auto n = fixed_point{-2.75}; 18 | static_assert(n==-2.75, "fixed-point type was unable to store the value"); 19 | //! [define a fixed_point value] 20 | } 21 | 22 | namespace define_a_fast_object_using_make_elastic { 23 | //! [define an int-sized object using make_elastic_fixed_point and const_integer] 24 | // std::uint8_t specifies the type of const_integer - not elastic 25 | constexpr auto n = make_elastic_fixed_point(const_integer{}); 26 | 27 | static_assert(n==0xAA, "n now has the value, 1024"); 28 | static_assert(std::is_same>::value, "by default make_elastic_fixed_point uses the most efficient type it can"); 29 | //! [define an int-sized object using make_elastic_fixed_point and const_integer] 30 | } 31 | 32 | namespace define_a_small_object_using_make_elastic { 33 | //! [define a byte-sized object using \ref make_elastic_fixed_point and \ref _c] 34 | constexpr auto n = make_elastic_fixed_point(const_integer{}); 35 | 36 | static_assert(n==1536, "n now has the value, 1536"); 37 | static_assert(std::is_same>::value, "by default make_elastic_fixed_point uses the most efficient type it can"); 38 | //! [define a byte-sized object using \ref make_elastic_fixed_point and \ref _c] 39 | } 40 | 41 | namespace define_a_fast_object_using_elastic_literal { 42 | //! [define an object using elastic literal] 43 | using namespace sg14::literals; 44 | constexpr auto n = 34_elastic; 45 | 46 | static_assert(n==34, "n now has the value, 1536"); 47 | static_assert(std::is_same>::value, "type only uses 1 bit of range"); 48 | //! [define an object using elastic literal] 49 | } 50 | 51 | namespace use_resize_1 { 52 | //! [use set_digits 1] 53 | using new_type = set_digits_t; 54 | static_assert(std::is_same::value, "failed to resize an unsigned int to 16-bytes"); 55 | //! [use set_digits 1] 56 | } 57 | 58 | namespace use_resize_2 { 59 | //! [use set_digits 2] 60 | using new_type = set_digits_t; 61 | static_assert(std::is_same::value, "failed to resize a signed char to a type of at least 35 bits"); 62 | //! [use set_digits 2] 63 | } 64 | 65 | namespace use_resize_3 { 66 | //! [use set_digits 3] 67 | using new_type = set_digits_t, 24>; 68 | static_assert(std::is_same>::value, "failed to resize a signed, 1-byte fixed-point type to a fixed-point type of at least 3 bytes"); 69 | //! [use set_digits 3] 70 | } 71 | 72 | namespace use_make_fixed { 73 | //! [use make_fixed] 74 | static_assert(std::is_same, fixed_point>::value, "failed to use make_fixed"); 75 | //! [use make_fixed] 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | User Manual {#mainpage} 2 | =========== 3 | 4 | \tableofcontents 5 | 6 | 7 | \section Introduction 8 | 9 | The fixed_point library is **deprecated**. 10 | Please consider using [CNL](http://johnmcfarlane.github.io/cnl/) instead. 11 | 12 | The [fixed_point](http://johnmcfarlane.github.io/fixed_point/) library provides 13 | a header-only C++11 API for approximating real numbers using binary fixed-point arithmetic. 14 | It forms the reference implementation for a standard library proposal presented in paper, [P0037](papers/p0037.html) 15 | and is developed as part of study groups, [SG14](https://groups.google.com/a/isocpp.org/forum/#!forum/sg14) and SG6. 16 | Installation instructions are provided on the [project page](https://github.com/johnmcfarlane/fixed_point). 17 | 18 | 19 | \subsection Features 20 | 21 | Header, [sg14/fixed_point](@ref fixed_point), defines class template, [fixed_point](@ref sg14::fixed_point), with: 22 | 23 | * a simple, flexible generic design; 24 | * operator overloads that interface [fixed_point](@ref sg14::fixed_point) with other numeric types; 25 | * a set of function templates, (e.g. [multiply](@ref sg14::multiply)), for fine-grain control. 26 | 27 | Header, [sg14/num_traits](@ref num_traits), contains additions (such as [set_digits](@ref sg14::set_digits)) 28 | that support widening of arithmetic types in order to deal with precision loss. 29 | 30 | Auxiliary modules: 31 | 32 | * [sg14/auxiliary/multiprecision.h](@ref multiprecision.h) adapts Boost.Multiprecision for use in fixed-point types; 33 | * [sg14/auxiliary/elastic_fixed_point.h](@ref elastic_fixed_point.h) defins [elastic_fixed_point](@ref sg14::elastic_fixed_point), an numeric type that uses fixed-point to avoid overflow. 34 | 35 | 36 | \section Examples 37 | 38 | The following examples 39 | [can be found](https://github.com/johnmcfarlane/fixed_point/blob/master/src/test/index.cpp) 40 | in the test suite. 41 | 42 | 43 | \subsection declaration Declaration 44 | 45 | The [fixed_point](@ref sg14::fixed_point) type extends the behavior of integer types in a natural way. 46 | It represents a real number using an integer type, scaled by a power of two. 47 | 48 | \snippet index.cpp declaration example 49 | 50 | 51 | \subsection basic_arithmetic Arithmetic Operators 52 | 53 | Specializations of [fixed_point](@ref sg14::fixed_point) behave a lot like native C/C++ numeric types. 54 | Operators are designed to behave in an way which is both predictable and efficient. 55 | 56 | \snippet index.cpp basic arithmetic example 57 | 58 | 59 | \subsection advanced_arithmetic Arithmetic Functions 60 | 61 | But one size does not fit all. 62 | Different applications of the same operation might call for different trade-offs between storage, precision, safety and speed. 63 | Named functions - such as [multiply](@ref sg14::multiply) - provide fine-tuned control over arithmetic results. 64 | 65 | \snippet index.cpp advanced arithmetic example 66 | 67 | 68 | \subsection elastic Extensible 69 | 70 | Because one size does not fit all, [fixed_point](@ref sg14::fixed_point) is designed to make it easy to tailor new arithmetic types. 71 | The [elastic_fixed_point](@ref sg14::elastic_fixed_point) type illustrates this. 72 | As each calculation requires more digits, so the results of [elastic_fixed_point](@ref sg14::elastic_fixed_point) operations allocate more storage. 73 | 74 | \snippet index.cpp elastic example 75 | -------------------------------------------------------------------------------- /src/test/num_traits.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | 13 | namespace { 14 | using sg14::_impl::identical; 15 | 16 | namespace test_digits_type { 17 | static_assert(std::is_same< 18 | typename std::remove_cv::digits)>::type, 19 | sg14::_digits_type>::value, "sg14::_digits_type test failed"); 20 | } 21 | 22 | namespace test_to_rep { 23 | using sg14::_impl::to_rep; 24 | 25 | static_assert(identical(to_rep(UINT64_C(0xFEDCBA9876543210)), UINT64_C(0xFEDCBA9876543210)), 26 | "sg14::numeric_traits<> test failed"); 27 | } 28 | 29 | namespace test_set_digits { 30 | using sg14::set_digits; 31 | static_assert(identical(sg14::set_digits::type{0}, std::int64_t{0}), ""); 32 | } 33 | 34 | namespace test_scale { 35 | using sg14::_impl::scale; 36 | 37 | static_assert(identical(scale(3, 2, 15), 98304U), 38 | "sg14::numeric_traits<> test failed"); 39 | 40 | static_assert(identical(scale(0b11110101, 2, 8), 0b1111010100000000), "sg14::scale test failed"); 41 | static_assert(scale(0b10110110, 2, 4) == 0b101101100000, "sg14::scale test failed"); 42 | static_assert(scale(0b00111010, 2, 2) == 0b11101000, "sg14::scale test failed"); 43 | static_assert(scale(0b11101011, 2, 0) == 0b11101011, "sg14::scale test failed"); 44 | static_assert(scale(0b01100100, 2, -2) == 0b00011001, "sg14::scale test failed"); 45 | static_assert(scale(0b00111001, 2, -4) == 0b00000011, "sg14::scale test failed"); 46 | static_assert(scale(0b10110011, 2, -8) == 0, "sg14::scale test failed"); 47 | 48 | static_assert(scale(-0b1110101, 2, 8) == -0b111010100000000, "sg14::scale test failed"); 49 | static_assert(scale(-0b0110110, 2, 4) == -0b01101100000, "sg14::scale test failed"); 50 | static_assert(scale(+0b0011010, 2, 2) == +0b1101000, "sg14::scale test failed"); 51 | static_assert(scale(-0b1101011, 2, 0) == -0b1101011, "sg14::scale test failed"); 52 | static_assert(scale(+0b1100100, 2, -2) == +0b0011001, "sg14::scale test failed"); 53 | static_assert(scale(+0b0111001, 2, -4) == +0b0000011, "sg14::scale test failed"); 54 | static_assert(scale(-0b0110011, 2, -8) == -0b0000000, "sg14::scale test failed"); 55 | 56 | static_assert(scale(1, 2, 30) == 0x40000000, "sg14::scale test failed"); 57 | static_assert(scale(1, 2, 4) == 16, "sg14::scale test failed"); 58 | } 59 | 60 | namespace test_is_integer_or_float { 61 | //////////////////////////////////////////////////////////////////////////////// 62 | // sg14::_impl::is_integer_or_float 63 | 64 | using sg14::_impl::is_integer_or_float; 65 | static_assert(is_integer_or_float::value, "sg14::_integer_impl::is_integer_or_float test failed"); 66 | static_assert(is_integer_or_float::value, "sg14::_integer_impl::is_integer_or_float test failed"); 67 | static_assert(!is_integer_or_float::value, "sg14::_integer_impl::is_integer_or_float test failed"); 68 | static_assert(!is_integer_or_float::value, "sg14::_integer_impl::is_integer_or_float test failed"); 69 | static_assert(!is_integer_or_float::value, "sg14::_integer_impl::is_integer_or_float test failed"); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/test/fixed_point_math_common.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright Timo Alho 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | //No include guards: we mean to include this for each case separately 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | TEST(math, FPTESTFORMAT) { 17 | using fp = sg14::fixed_point; 18 | 19 | //Test integer powers 20 | for (int i = 0; i < fp::integer_digits; i++) { 21 | ASSERT_EQ(exp2(fp{ i }), fp{ 1 << i }); 22 | } 23 | 24 | //Test negative integer powers (which are representable in the format) 25 | #if (FPTESTEXP < 0) 26 | for (int i = std::max(-fp::fractional_digits, -(sg14::_impl::shift_left(1)) + 1); i < std::min(0, fp::integer_digits - 1); i++) { 27 | fp lhs{ exp2(fp{ i }) }; 28 | EXPECT_EQ(lhs, fp::from_data(1 << (-fp::exponent + i))) 29 | << "i = " << i << ", fixed point raw: " << lhs.data() << " should be: " << (1 << (-fp::exponent + i)) 30 | ; 31 | } 32 | #endif 33 | 34 | //Select a number of fractions to test for each integer power 35 | //TODO: think if there are special values that would be most likely to fail 36 | //TODO: it should be possible in a non-routine unit test to test over all 37 | //2^32 values of a 32-bit integer 38 | constexpr std::array fracts{ { 39 | static_cast(std::numeric_limits::min()), //As close to zero as possible 40 | 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 41 | sg14::_impl::min(1., static_cast(std::numeric_limits::max())) //As close to one as possible 42 | } }; 43 | 44 | for (int i = -fp::fractional_digits; i < fp::integer_digits; i++) { 45 | for (double frac : fracts) { 46 | 47 | //Build the double so that it's 48 | //guaranteed to match with the 49 | //fixed point representation 50 | //of the exponent, 51 | //i.e. the input to the function should 52 | //have the same rounding error as the number 53 | //being tested 54 | fp fprep{ i + frac }; 55 | double doublerep{ fprep }; 56 | 57 | //Check for at most 1 LSB error 58 | fp lhs{ exp2(fp{ fprep }) }; 59 | fp rhs{ exp2(doublerep) }; //Will use the double overload 60 | EXPECT_LE(std::abs(lhs.data() - rhs.data()), 1) 61 | << "fail at " << i + frac << ", fixed point raw: " << lhs.data() << " double raw " << rhs.data() 62 | ; 63 | //bit-accurate:: not without a rounding multiply 64 | //EXPECT_EQ(exp2(fp{fprep}), fp{exp2(doublerep)}); 65 | } 66 | } 67 | 68 | using numeric_limits = std::numeric_limits; 69 | if (numeric_limits::max() >= int(fp::integer_digits) 70 | && numeric_limits::lowest() <= -fp::fractional_digits) { 71 | //the largest exponent which's result doesn't overflow 72 | auto maximum = fp::from_data(fp{ fp::integer_digits }.data() - 1); 73 | 74 | //The next-to-smallest exponent whose result doesn't overflow 75 | //(The very smallest was already tested with the integer exponents) 76 | auto minimum = fp::from_data(fp{ -fp::fractional_digits }.data() + 1); 77 | 78 | double doublerep{ maximum }; 79 | double doublerepmini{ minimum }; 80 | 81 | EXPECT_LE(std::abs(exp2(maximum).data() - fp{ exp2(doublerep) }.data()), 1) 82 | << "fixed point raw: " << exp2(maximum).data() << ", double raw: " << fp{ exp2(doublerep) }.data(); 83 | 84 | EXPECT_EQ(exp2(minimum), fp{ exp2(doublerepmini) }); 85 | } 86 | } 87 | 88 | -------------------------------------------------------------------------------- /include/sg14/bits/fixed_point_common_type.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief `sg14::fixed_point` specializations of \c std::common_type 9 | 10 | #if !defined(SG14_FIXED_POINT_COMMON_TYPE_H) 11 | #define SG14_FIXED_POINT_COMMON_TYPE_H 1 12 | 13 | #include "fixed_point_type.h" 14 | 15 | /// study group 14 of the C++ working group 16 | namespace sg14 { 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | // implementation-specific definitions 20 | 21 | namespace _impl { 22 | namespace fp { 23 | namespace ct { 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | // sg14::_impl::fp::ct::common_type_mixed 27 | 28 | template 29 | struct common_type_mixed; 30 | 31 | // given a fixed-point and an integer type, 32 | // generates a fixed-point type that is as big as both of them (or as close as possible) 33 | template 34 | struct common_type_mixed, RhsInteger, _impl::enable_if_t::is_integer>> : std::common_type< 36 | fixed_point, fixed_point> { 37 | }; 38 | 39 | // given a fixed-point and a floating-point type, 40 | // generates a floating-point type that is as big as both of them (or as close as possible) 41 | template 42 | struct common_type_mixed< 43 | fixed_point, Float, 44 | _impl::enable_if_t::value>> 45 | : std::common_type<_impl::fp::float_of_same_size, Float> { 46 | }; 47 | } 48 | } 49 | } 50 | } 51 | 52 | namespace std { 53 | //////////////////////////////////////////////////////////////////////////////// 54 | // std::common_type<> specializations related to sg14::sg14::fixed_point<> 55 | 56 | // std::common_type> 57 | template 58 | struct common_type> { 59 | using type = sg14::fixed_point< 60 | typename std::common_type::type, 61 | Exponent>; 62 | }; 63 | 64 | // std::common_type, not-fixed-point> 65 | template 66 | struct common_type, Rhs> { 67 | static_assert(!sg14::_impl::is_fixed_point::value, "fixed-point Rhs type"); 68 | using type = typename sg14::_impl::fp::ct::common_type_mixed, Rhs>::type; 69 | }; 70 | 71 | // std::common_type> 72 | template 73 | struct common_type> { 74 | static_assert(!sg14::_impl::is_fixed_point::value, "fixed-point Lhs type"); 75 | using type = typename sg14::_impl::fp::ct::common_type_mixed, Lhs>::type; 76 | }; 77 | 78 | // std::common_type, fixed_point<>> 79 | template 80 | struct common_type, sg14::fixed_point> { 81 | using type = sg14::fixed_point, sg14::_impl::min(LhsExponent, RhsExponent)>; 82 | }; 83 | } 84 | 85 | #endif // SG14_FIXED_POINT_COMMON_TYPE_H 86 | -------------------------------------------------------------------------------- /include/sg14/bits/fixed_point_named.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief essential named definitions related to the `sg14::fixed_point` type 9 | 10 | #if !defined(SG14_FIXED_POINT_NAMED_H) 11 | #define SG14_FIXED_POINT_NAMED_H 1 12 | 13 | #include "common.h" 14 | 15 | #include "fixed_point_arithmetic.h" 16 | 17 | /// study group 14 of the C++ working group 18 | namespace sg14 { 19 | 20 | /// \brief calculates the negative of a \ref fixed_point value 21 | /// \headerfile sg14/fixed_point 22 | /// 23 | /// \param rhs input value 24 | /// 25 | /// \return negative: - rhs 26 | /// 27 | /// \note This function negates the value 28 | /// without performing any additional scaling or conversion. 29 | /// 30 | /// \sa add, subtract, multiply, divide 31 | 32 | template 33 | constexpr auto negate(const fixed_point& rhs) 34 | -> fixed_point 35 | { 36 | using result_type = fixed_point; 37 | return result_type::from_data(-rhs.data()); 38 | } 39 | 40 | /// \brief calculates the sum of two \ref fixed_point values 41 | /// \headerfile sg14/fixed_point 42 | /// 43 | /// \param lhs, rhs augend and addend 44 | /// 45 | /// \return sum: lhs + rhs 46 | /// 47 | /// \note This function add the values 48 | /// without performing any additional scaling or conversion. 49 | /// 50 | /// \sa negate, subtract, multiply, divide 51 | 52 | template 53 | constexpr auto add(const Lhs& lhs, const Rhs& rhs) 54 | -> decltype(_impl::fp::operate<_impl::fp::named_function_tag>(lhs, rhs, _impl::add_tag)) 55 | { 56 | return _impl::fp::operate<_impl::fp::named_function_tag>(lhs, rhs, _impl::add_tag); 57 | } 58 | 59 | /// \brief calculates the difference of two \ref fixed_point values 60 | /// \headerfile sg14/fixed_point 61 | /// 62 | /// \param lhs, rhs minuend and subtrahend 63 | /// 64 | /// \return difference: lhs - rhs 65 | /// 66 | /// \note This function subtracts the values 67 | /// without performing any additional scaling or conversion. 68 | /// 69 | /// \sa negate, add, multiply, divide 70 | 71 | template 72 | constexpr auto subtract(const Lhs& lhs, const Rhs& rhs) 73 | -> decltype(_impl::fp::operate<_impl::fp::named_function_tag>(lhs, rhs, _impl::subtract_tag)) 74 | { 75 | return _impl::fp::operate<_impl::fp::named_function_tag>(lhs, rhs, _impl::subtract_tag); 76 | } 77 | 78 | /// \brief calculates the product of two \ref fixed_point factors 79 | /// \headerfile sg14/fixed_point 80 | /// 81 | /// \param lhs, rhs the factors 82 | /// 83 | /// \return product: lhs * rhs 84 | /// 85 | /// \note This function multiplies the values 86 | /// without performing any additional scaling or conversion. 87 | /// 88 | /// \sa negate, add, subtract, divide 89 | 90 | template 91 | constexpr auto multiply(const Lhs& lhs, const Rhs& rhs) 92 | -> decltype(_impl::fp::operate<_impl::fp::named_function_tag>(lhs, rhs, _impl::multiply_tag)) 93 | { 94 | return _impl::fp::operate<_impl::fp::named_function_tag>(lhs, rhs, _impl::multiply_tag); 95 | } 96 | 97 | /// \brief calculates the quotient of two \ref fixed_point values 98 | /// \headerfile sg14/fixed_point 99 | /// 100 | /// \param lhs, rhs dividend and divisor 101 | /// 102 | /// \return quotient: lhs / rhs 103 | /// 104 | /// \note This function divides the values 105 | /// without performing any additional scaling or conversion. 106 | /// 107 | /// \sa negate, add, subtract, multiply 108 | 109 | template 110 | constexpr auto divide(const Lhs& lhs, const Rhs& rhs) 111 | -> decltype(_impl::fp::operate<_impl::fp::division_named_function_tag>(lhs, rhs, _impl::divide_tag)) 112 | { 113 | return _impl::fp::operate<_impl::fp::division_named_function_tag>(lhs, rhs, _impl::divide_tag); 114 | } 115 | } 116 | 117 | #endif // SG14_FIXED_POINT_NAMED_H 118 | -------------------------------------------------------------------------------- /doc/Doxyfile: -------------------------------------------------------------------------------- 1 | PROJECT_NAME = "fixed_point (deprecated)" 2 | PROJECT_BRIEF = "Binary Fixed-Point Arithmetic Library in C++" 3 | PROJECT_LOGO = 4 | PROJECT_NUMBER = rev.2 5 | 6 | STRIP_FROM_PATH = ../include 7 | BUILTIN_STL_SUPPORT = YES 8 | STRIP_FROM_INC_PATH = ../include 9 | ALIASES = 10 | ENABLED_SECTIONS = 11 | MARKDOWN_SUPPORT = YES 12 | CALL_GRAPH = NO 13 | CALLER_GRAPH = NO 14 | 15 | # Resources 16 | OUTPUT_DIRECTORY = 17 | OUTPUT_LANGUAGE = English 18 | INPUT = \ 19 | ../include/sg14/auxiliary/const_integer.h \ 20 | ../include/sg14/auxiliary/elastic_fixed_point.h \ 21 | ../include/sg14/auxiliary/elastic_integer.h \ 22 | ../include/sg14/auxiliary/multiprecision.h \ 23 | ../include/sg14/auxiliary/numeric.h \ 24 | ../include/sg14/auxiliary/overflow.h \ 25 | ../include/sg14/auxiliary/precise_integer.h \ 26 | ../include/sg14/auxiliary/safe_integer.h \ 27 | ../include/sg14/fixed_point \ 28 | ../include/sg14/bits/fixed_point_arithmetic.h \ 29 | ../include/sg14/bits/fixed_point_extras.h \ 30 | ../include/sg14/bits/fixed_point_math.h \ 31 | ../include/sg14/bits/fixed_point_make.h \ 32 | ../include/sg14/bits/fixed_point_named.h \ 33 | ../include/sg14/bits/fixed_point_operators.h \ 34 | ../include/sg14/bits/fixed_point_common_type.h \ 35 | ../include/sg14/bits/fixed_point_type.h \ 36 | ../include/sg14/bits/type_traits.h \ 37 | ../include/sg14/bits/number_base.h \ 38 | ../include/sg14/bits/config.h \ 39 | ../include/sg14/bits/common.h \ 40 | ../include/sg14/bits/limits.h \ 41 | ../include/sg14/num_traits.h \ 42 | index.md 43 | EXCLUDE_SYMBOLS = \ 44 | sg14::_* 45 | FILE_PATTERNS = * 46 | RECURSIVE = YES 47 | EXCLUDE = 48 | EXAMPLE_PATH = ../src/test 49 | EXAMPLE_RECURSIVE = YES 50 | IMAGE_PATH = 51 | WARN_IF_UNDOCUMENTED = YES 52 | USE_MDFILE_AS_MAINPAGE = index.md 53 | 54 | SHOW_GROUPED_MEMB_INC = YES 55 | BRIEF_MEMBER_DESC = YES 56 | REPEAT_BRIEF = YES 57 | ALWAYS_DETAILED_SEC = NO 58 | INLINE_INHERITED_MEMB = NO 59 | JAVADOC_AUTOBRIEF = YES 60 | QT_AUTOBRIEF = YES 61 | MULTILINE_CPP_IS_BRIEF = YES 62 | INHERIT_DOCS = NO 63 | SEPARATE_MEMBER_PAGES = NO 64 | DISTRIBUTE_GROUP_DOC = NO 65 | SUBGROUPING = NO 66 | INLINE_GROUPED_CLASSES = NO 67 | INLINE_SIMPLE_STRUCTS = NO 68 | 69 | # Generated formats 70 | GENERATE_HTML = YES 71 | GENERATE_LATEX = NO 72 | 73 | 74 | GENERATE_TODOLIST = YES 75 | GENERATE_TESTLIST = YES 76 | GENERATE_BUGLIST = YES 77 | GENERATE_DEPRECATEDLIST = YES 78 | SHOW_USED_FILES = NO 79 | SHOW_FILES = NO 80 | SHOW_NAMESPACES = YES 81 | LAYOUT_FILE = 82 | 83 | 84 | CLASS_DIAGRAMS = YES 85 | HAVE_DOT = NO 86 | 87 | HIDE_UNDOC_RELATIONS = NO 88 | HIDE_UNDOC_MEMBERS = YES 89 | HIDE_UNDOC_CLASSES = YES 90 | HIDE_FRIEND_COMPOUNDS = NO 91 | HIDE_IN_BODY_DOCS = NO 92 | INTERNAL_DOCS = NO 93 | HIDE_SCOPE_NAMES = NO 94 | SHOW_INCLUDE_FILES = YES 95 | FORCE_LOCAL_INCLUDES = NO 96 | INLINE_INFO = NO 97 | SORT_MEMBER_DOCS = YES 98 | SORT_BRIEF_DOCS = YES 99 | SORT_MEMBERS_CTORS_1ST = YES 100 | SORT_GROUP_NAMES = NO 101 | SORT_BY_SCOPE_NAME = YES 102 | 103 | 104 | ALPHABETICAL_INDEX = NO 105 | COLS_IN_ALPHA_INDEX = 1 106 | 107 | # Preprocessing 108 | ENABLE_PREPROCESSING = NO 109 | MACRO_EXPANSION = YES 110 | EXPAND_ONLY_PREDEF = NO 111 | SEARCH_INCLUDES = YES 112 | INCLUDE_PATH = ../include 113 | INCLUDE_FILE_PATTERNS = 114 | PREDEFINED = 115 | SKIP_FUNCTION_MACROS = NO 116 | 117 | # Source browsing 118 | SOURCE_BROWSER = NO 119 | INLINE_SOURCES = NO 120 | STRIP_CODE_COMMENTS = YES 121 | REFERENCED_BY_RELATION = YES 122 | REFERENCES_RELATION = YES 123 | REFERENCES_LINK_SOURCE = YES 124 | USE_HTAGS = NO 125 | VERBATIM_HEADERS = YES 126 | # CLANG_ASSISTED_PARSING = NO 127 | # CLANG_OPTIONS = 128 | 129 | # HTML output 130 | HTML_OUTPUT = gh-pages 131 | HTML_FILE_EXTENSION = .html 132 | HTML_HEADER = 133 | HTML_FOOTER = 134 | HTML_EXTRA_STYLESHEET = 135 | HTML_EXTRA_FILES = 136 | HTML_COLORSTYLE_HUE = 75 # 0 - 359 137 | HTML_COLORSTYLE_SAT = 110 # 0 - 255 138 | HTML_COLORSTYLE_GAMMA = 80 139 | HTML_TIMESTAMP = NO 140 | HTML_DYNAMIC_SECTIONS = YES 141 | HTML_INDEX_NUM_ENTRIES = 0 # Fully expand trees in the Indexes by default 142 | #DISABLE_INDEX = YES 143 | #GENERATE_TREEVIEW = YES 144 | #TREEVIEW_WIDTH = 270 145 | #EXT_LINKS_IN_WINDOW = NO 146 | #FORMULA_FONTSIZE = 10 147 | #FORMULA_TRANSPARENT = YES 148 | #SEARCHENGINE = YES 149 | 150 | # Mathjax (HTML only) 151 | USE_MATHJAX = NO 152 | #MATHJAX_FORMAT = HTML-CSS 153 | #MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest 154 | #MATHJAX_EXTENSIONS = 155 | #MATHJAX_CODEFILE = 156 | -------------------------------------------------------------------------------- /src/test/zero_cost_average.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief Signed 15:16 Fixed-Point Average Function Using sg14::elastic_fixed_point 9 | 10 | #include 11 | #include 12 | 13 | using namespace sg14; 14 | 15 | #if (__cplusplus>=201402L) 16 | #define CONSTEXPR constexpr 17 | #else 18 | #define CONSTEXPR 19 | #endif 20 | 21 | // average two nunbers using 15:16 fixed-point arithmetic using native types 22 | CONSTEXPR float average_integer(float input1, float input2) { 23 | // user must scale values by the correct amount 24 | auto fixed1 = static_cast(input1 * 65536.f); 25 | auto fixed2 = static_cast(input2 * 65536.f); 26 | 27 | // user must remember to widen the result to avoid overflow 28 | auto sum = int64_t{fixed1} + fixed2; 29 | auto avg = sum / 2; 30 | 31 | // user must remember that the scale also was squared 32 | return avg / 65536.f; 33 | } 34 | 35 | // the same function using sg14::elastic_integer - 36 | // a numeric type which widens to avoid overflow 37 | CONSTEXPR float average_elastic_integer(float input1, float input2) { 38 | // use this user-defined literal - shorthand for std::integral_constant 39 | using namespace literals; 40 | 41 | // elastic_integer behaves a lot like native ints 42 | auto fixed1 = elastic_integer<31>{input1 * 65536.f}; 43 | auto fixed2 = elastic_integer<31>{input2 * 65536.f}; 44 | 45 | // sum is one bit wider to reduce risk of overflow 46 | auto sum = fixed1 + fixed2; 47 | auto avg = sum / 2_c; 48 | 49 | // but the user must still do all the scaling themselves 50 | return static_cast(avg) / 65536.f; 51 | } 52 | 53 | // the same function using sg14::fixed_point 54 | CONSTEXPR float average_fixed_point(float input1, float input2) { 55 | // fixed_point handles scaling 56 | auto fixed1 = fixed_point{input1}; 57 | auto fixed2 = fixed_point{input2}; 58 | 59 | // but it uses int under the hood; user must still widen 60 | auto sum = fixed_point{fixed1} + fixed2; 61 | auto avg = divide(sum, 2); 62 | 63 | return static_cast(avg); 64 | } 65 | 66 | // finally, the composition of fixed_point and elastic_integer 67 | CONSTEXPR float average_elastic(float input1, float input2) { 68 | // define optimally-scaled quantity types with this user-defined literal; 69 | // e.g. 65536_elastic uses 2 bits of storage 70 | // and 1_elastic/65536_elastic uses 3 bits of storage! 71 | using namespace literals; 72 | 73 | // elastic_fixed_point<15, 16> aliases to fixed_point, -16> 74 | auto fixed1 = elastic_fixed_point<15, 16>{input1}; 75 | auto fixed2 = elastic_fixed_point<15, 16>{input2}; 76 | 77 | // concise, overflow-resistant and zero-cost! 78 | auto sum = fixed1 + fixed2; 79 | auto avg = sum / 2_elastic; 80 | 81 | return static_cast(avg); 82 | } 83 | 84 | using namespace literals; 85 | using sg14::_impl::identical; 86 | static_assert(identical(65536_elastic, elastic_fixed_point<17, -16>{65536}), "mistaken comment in average_elastic"); 87 | static_assert(identical(1_elastic/65536_elastic, elastic_fixed_point<-15, 17>{0.0000152587890625}), "mistaken comment in average_elastic"); 88 | 89 | #if (__cplusplus>=201402L) 90 | static_assert(identical(average_integer(32000.125, 27805.75), 29902.9375f), "average_integer test failed"); 91 | static_assert(identical(average_elastic_integer(32000.125, 27805.75), 29902.9375f), "average_elastic_integer test failed"); 92 | static_assert(identical(average_fixed_point(32000.125, 27805.75), 29902.9375f), "average_fixed_point test failed"); 93 | static_assert(identical(average_elastic(32000.125, 27805.75), 29902.9375f), "average_elastic test failed"); 94 | #else 95 | TEST(zero_cost_average, integer) { 96 | ASSERT_EQ(average_integer(32000.125, 27805.75), 29902.9375f); 97 | } 98 | 99 | TEST(zero_cost_average, elastic_integer) { 100 | ASSERT_EQ(average_elastic_integer(30000, 0.125), 15000.0625f); 101 | } 102 | 103 | TEST(zero_cost_average, fixed_point) { 104 | ASSERT_EQ(average_fixed_point(30000, 0.125), 15000.0625f); 105 | } 106 | 107 | TEST(zero_cost_average, elastic_fixed_point) { 108 | ASSERT_EQ(average_elastic(30000, 0.125), 15000.0625f); 109 | } 110 | #endif 111 | -------------------------------------------------------------------------------- /include/sg14/auxiliary/multiprecision.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief definitions and specializations that adapt Boost.Multiprecision for use with @ref sg14::fixed_point 9 | 10 | #if !defined(SG14_MULTIPRECISION_H) 11 | #define SG14_MULTIPRECISION_H 1 12 | 13 | #include 14 | 15 | #include 16 | 17 | /// study group 14 of the C++ working group 18 | namespace sg14 { 19 | namespace _bmp = boost::multiprecision; 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | //////////////////////////////////////////////////////////////////////////////// 23 | // type trait specializations of boost::multiprecision types 24 | // 25 | // These are the definitions needed to use any custom integer type with 26 | // sg14::fixed_point 27 | 28 | template 29 | struct make_signed<_bmp::cpp_int_backend> { 30 | using type = _bmp::cpp_int_backend; 31 | }; 32 | 33 | template 34 | struct make_unsigned<_bmp::cpp_int_backend> { 35 | using type = _bmp::cpp_int_backend; 36 | }; 37 | 38 | template 39 | struct digits<_bmp::cpp_int_backend> 40 | : std::integral_constant<_digits_type, NumBits> { 41 | }; 42 | 43 | template 44 | struct set_digits<_bmp::cpp_int_backend, MinNumDigits> { 45 | static constexpr unsigned width = MinNumDigits + (SignType == _bmp::signed_magnitude); 46 | using type = _bmp::cpp_int_backend; 47 | }; 48 | 49 | template 50 | struct make_signed<_bmp::number> { 51 | using type = _bmp::number, ExpressionTemplates>; 52 | }; 53 | 54 | template 55 | struct make_unsigned<_bmp::number> { 56 | using type = _bmp::number, ExpressionTemplates>; 57 | }; 58 | 59 | template 60 | struct digits<_bmp::number> 61 | : digits { 62 | }; 63 | 64 | template 65 | struct set_digits<_bmp::number, MinNumDigits> { 66 | using type = _bmp::number, ExpressionTemplates>; 67 | }; 68 | 69 | //////////////////////////////////////////////////////////////////////////////// 70 | //////////////////////////////////////////////////////////////////////////////// 71 | // aliases of _bmp types 72 | 73 | namespace _sized_integer_impl { 74 | template 75 | using backend = _bmp::cpp_int_backend< 76 | NumBits, NumBits, SignType, _bmp::unchecked, void>; 77 | } 78 | 79 | // sg14::signed_multiprecision - a signed integer of arbitrary size 80 | template::value> 81 | using signed_multiprecision = _bmp::number<_sized_integer_impl::backend, _bmp::et_off>; 82 | 83 | // sg14::unsigned_multiprecision - an unsigned integer of arbitrary size 84 | template::value> 85 | using unsigned_multiprecision = _bmp::number<_sized_integer_impl::backend, _bmp::et_off>; 86 | 87 | // sg14::unsigned_multiprecision - an integer of arbitrary size 88 | template::value> 89 | using multiprecision = signed_multiprecision; 90 | } 91 | 92 | #endif // SG14_MULTIPRECISION_H 93 | -------------------------------------------------------------------------------- /include/sg14/auxiliary/boost.multiprecision.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief definitions and specializations that adapt Boost.Multiprecision for use with @ref sg14::fixed_point 9 | 10 | #if !defined(SG14_BOOST_MULTIPRECISION_H) 11 | #define SG14_BOOST_MULTIPRECISION_H 1 12 | 13 | #include 14 | 15 | #include 16 | 17 | /// study group 14 of the C++ working group 18 | namespace sg14 { 19 | namespace _bmp = boost::multiprecision; 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | //////////////////////////////////////////////////////////////////////////////// 23 | // type trait specializations of boost::multiprecision types 24 | // 25 | // These are the definitions needed to use any custom integer type with 26 | // sg14::fixed_point 27 | 28 | template 29 | struct make_signed<_bmp::cpp_int_backend> { 30 | using type = _bmp::cpp_int_backend; 31 | }; 32 | 33 | template 34 | struct make_unsigned<_bmp::cpp_int_backend> { 35 | using type = _bmp::cpp_int_backend; 36 | }; 37 | 38 | template 39 | struct digits<_bmp::cpp_int_backend> 40 | : std::integral_constant<_digits_type, NumBits> { 41 | }; 42 | 43 | template 44 | struct set_digits<_bmp::cpp_int_backend, MinNumDigits> { 45 | static constexpr unsigned width = MinNumDigits + (SignType == _bmp::signed_magnitude); 46 | using type = _bmp::cpp_int_backend; 47 | }; 48 | 49 | template 50 | struct make_signed<_bmp::number> { 51 | using type = _bmp::number, ExpressionTemplates>; 52 | }; 53 | 54 | template 55 | struct make_unsigned<_bmp::number> { 56 | using type = _bmp::number, ExpressionTemplates>; 57 | }; 58 | 59 | template 60 | struct digits<_bmp::number> 61 | : digits { 62 | }; 63 | 64 | template 65 | struct set_digits<_bmp::number, MinNumDigits> { 66 | using type = _bmp::number, ExpressionTemplates>; 67 | }; 68 | 69 | //////////////////////////////////////////////////////////////////////////////// 70 | //////////////////////////////////////////////////////////////////////////////// 71 | // aliases of _bmp types 72 | 73 | namespace _sized_integer_impl { 74 | template 75 | using backend = _bmp::cpp_int_backend< 76 | NumBits, NumBits, SignType, _bmp::unchecked, void>; 77 | } 78 | 79 | // sg14::signed_multiprecision - a signed integer of arbitrary size 80 | template::value> 81 | using signed_multiprecision = _bmp::number<_sized_integer_impl::backend, _bmp::et_off>; 82 | 83 | // sg14::unsigned_multiprecision - an unsigned integer of arbitrary size 84 | template::value> 85 | using unsigned_multiprecision = _bmp::number<_sized_integer_impl::backend, _bmp::et_off>; 86 | 87 | // sg14::unsigned_multiprecision - an integer of arbitrary size 88 | template::value> 89 | using multiprecision = signed_multiprecision; 90 | } 91 | 92 | #endif // SG14_BOOST_MULTIPRECISION_H 93 | -------------------------------------------------------------------------------- /src/test/const_integer.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief file containing tests of the `sg14/auxiliary/const_integer.h` definitions 9 | 10 | #include 11 | #include 12 | 13 | namespace { 14 | using sg14::_impl::identical; 15 | 16 | namespace test_const_integer_impl { 17 | 18 | namespace test_digits_to_integral { 19 | using sg14::_const_integer_impl::digits_to_integral; 20 | 21 | static_assert(identical(digits_to_integral<'0'>::value, INTMAX_C(0)), 22 | "sg14::_const_integer_impl::digits_to_integral test failed"); 23 | static_assert(identical(digits_to_integral<'1'>::value, INTMAX_C(1)), 24 | "sg14::_const_integer_impl::digits_to_integral test failed"); 25 | } 26 | 27 | namespace test_operate { 28 | using namespace sg14; 29 | using namespace literals; 30 | using namespace _impl; 31 | using _const_integer_impl::operate; 32 | static_assert( 33 | identical(operate( 34 | const_integer{}, 35 | const_integer{}, _impl::add_tag), 36 | const_integer{}), 37 | "sg14::_const_integer_impl::digits_to_integral test failed"); 38 | static_assert(identical(_const_integer_impl::operate(777, 10_c, _impl::divide_tag), INTMAX_C(77)), 39 | "sg14::elastic_integer test failed"); 40 | } 41 | } 42 | 43 | namespace test_const_integer { 44 | using sg14::const_integer; 45 | 46 | static_assert( 47 | identical(const_integer{}, const_integer{}), 48 | "sg14::const_integer construction test failed"); 49 | 50 | // unary minus 51 | static_assert( 52 | identical(-const_integer{}, const_integer{}), 53 | "sg14::const_integer unary minus test failed"); 54 | static_assert( 55 | identical(-const_integer{}, const_integer{}), 56 | "sg14::const_integer unary minus test failed"); 57 | #if ! defined(_MSC_VER) 58 | static_assert( 59 | identical(-const_integer{}, const_integer::max()>{}), 60 | "sg14::const_integer unary minus test failed"); 61 | #endif 62 | 63 | // binary plus 64 | static_assert(identical(const_integer{} + const_integer{}, const_integer{}), "sg14::const_integer addition test failed"); 65 | 66 | // divide 67 | using namespace sg14::literals; 68 | static_assert(identical(777/ 10_c, INTMAX_C(77)), 69 | "sg14::elastic_integer test failed"); 70 | 71 | // conversion to int 72 | static_assert(identical(static_cast(const_integer{}), 77213), "sg14::const_integer test failed"); 73 | } 74 | 75 | namespace test_literals { 76 | using namespace sg14::literals; 77 | using sg14::const_integer; 78 | 79 | static_assert(identical(0_c, const_integer()), 80 | "sg14::literals test failed"); 81 | static_assert(identical(1_c, const_integer()), 82 | "sg14::literals test failed"); 83 | static_assert(identical(2_c, const_integer()), 84 | "sg14::literals test failed"); 85 | static_assert(3_c == const_integer(), 86 | "sg14::literals test failed"); 87 | static_assert(identical(13971581_c, const_integer()), 88 | "sg14::literals test failed"); 89 | static_assert(identical(9223372036854775807_c, const_integer()), 90 | "sg14::literals test failed"); 91 | 92 | static_assert(identical(0x10000_c, const_integer()), 93 | "sg14::literals test failed"); 94 | static_assert(identical(0x91827364564738_c, const_integer()), 95 | "sg14::literals test failed"); 96 | 97 | static_assert( 98 | identical(-1_c, const_integer{}), 99 | "sg14::const_integer addition test failed"); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fixed_point Numerics Library 2 | 3 | [![Build Status](https://travis-ci.org/johnmcfarlane/fixed_point.svg?branch=develop)](https://travis-ci.org/johnmcfarlane/fixed_point) 4 | [![Build status](https://ci.appveyor.com/api/projects/status/p60lpkq9u90h83fi/branch/develop?svg=true)](https://ci.appveyor.com/project/johnmcfarlane/fixed-point/branch/develop) 5 | 6 | **Notice**: *This library is deprecated.* 7 | Please consider switching to [CNL](https://github.com/johnmcfarlane/cnl), the successor to *fixed_point* for many new features and improvements. 8 | 9 | ## Description 10 | 11 | The [fixed_point](https://github.com/johnmcfarlane/fixed_point) library provides 12 | a header-only C++11 API for approximating real numbers using binary fixed-point arithmetic. 13 | It forms the reference implementation of a standard library proposal presented in paper, [P0037](doc/p0037.md) 14 | and is developed as part of study groups, [SG14](https://groups.google.com/a/isocpp.org/forum/#!forum/sg14) and SG6. 15 | 16 | ## Download 17 | 18 | The library is [hosted](https://github.com/johnmcfarlane/fixed_point) on GitHub: 19 | 20 | ```shell 21 | $ git clone https://github.com/johnmcfarlane/fixed_point.git 22 | ``` 23 | 24 | The API is exposed through headers in the [include](./include/) directory. 25 | Add this to your system header list and include, e.g.: 26 | 27 | ```c++ 28 | #include 29 | ``` 30 | 31 | ## Tests and Benchmarks 32 | 33 | ### Linux 34 | 35 | Tested on [Travis](https://travis-ci.org/johnmcfarlane/fixed_point) (Ubuntu 14.04) using GCC 5.4 and Clang 3.5 36 | and [Debian GNU/Linux 8.3](https://www.debian.org/releases/stable/) using GCC 5.4 and Clang 3.5.0. 37 | 38 | Requires: 39 | 40 | - G++ 5.4 41 | - [CMake](https://cmake.org/download/) 3.2 42 | 43 | Optional: 44 | 45 | - [Boost](http://www.boost.org/) - facilitates multiprecision support 46 | - [Doxygen](http://www.doxygen.org/) - generates documentation in the *doc/gh-pages* directory 47 | - [pandoc](http://pandoc.org/) - generates proposal papers 48 | 49 | For a list of configuration options: 50 | 51 | ```shell 52 | $ cmake -LH 53 | ``` 54 | 55 | To build everything: 56 | 57 | ```shell 58 | $ cmake -DCMAKE_BUILD_TYPE=Release 59 | $ make 60 | ``` 61 | 62 | To disable exception handling (incompatible with Boost 1.55 or lower), add `-DEXCEPTIONS=OFF` to the `cmake` command: 63 | 64 | ```shell 65 | $ cmake -DCMAKE_BUILD_TYPE=Release -DEXCEPTIONS=OFF 66 | $ make 67 | ``` 68 | 69 | To run tests: 70 | 71 | ```shell 72 | $ cmake -DCMAKE_BUILD_TYPE=Release 73 | $ make fp_test 74 | $ ./fp_test 75 | ``` 76 | 77 | To run benchmarks: 78 | 79 | ```shell 80 | $ cmake -DCMAKE_BUILD_TYPE=Release 81 | $ make fp_benchmark 82 | $ ./fp_benchmark 83 | ``` 84 | 85 | To profile benchmarks: 86 | 87 | 1. Build with frame pointers included: 88 | 89 | ```shell 90 | $ cmake -DCMAKE_BUILD_TYPE=Release -DPROFILE=ON 91 | $ make fp_benchmark 92 | $ ./fp_benchmark 93 | ``` 94 | 95 | 2. then run: 96 | 97 | ```shell 98 | $ perf record -g ./fp_benchmark 99 | $ perf report -g 'graph,0.5,caller' 100 | ``` 101 | 102 | To install: 103 | 104 | ```shell 105 | $ cmake -DCMAKE_BUILD_TYPE=Release 106 | $ make 107 | $ sudo make install 108 | ``` 109 | 110 | #### Windows 111 | 112 | Tested on [AppVeyor](https://ci.appveyor.com/project/johnmcfarlane/fixed-point/branch/master) 113 | and on *Windows 10 Professional* with *CMake 3.8.0*. Requires: 114 | 115 | - MSBuild 15.0 (VS 2017) 116 | - [CMake](https://cmake.org/download/) 3.8.0 117 | 118 | To build *vs/Release/fp_test.exe* and *vs/Release/fp_benchmark.exe*: 119 | 120 | cmake -G "Visual Studio 15 2017" . 121 | MSBuild.exe /m fixed_point.sln /p:Configuration=Release 122 | 123 | For 64-bit builds, append `Win64` to the `-G` option above: 124 | 125 | cmake -G "Visual Studio 15 2017 Win64" . 126 | 127 | Note that *fp_benchmark* is unlikely to produce valid results due to missing `escape` and `clobber` functions. 128 | 129 | ### Cleaning 130 | 131 | To clean the project files: 132 | 133 | git clean -Xdff . 134 | 135 | (Use with caution!) 136 | 137 | ## Further Reading 138 | 139 | - [documentation](http://johnmcfarlane.github.io/fixed_point/) 140 | - [latest draft](doc/p0037.md) of C++ Standard Library proposal, [P0037](doc/p0037.md) 141 | - position paper illustrating the compositonal approach, [P0554](doc/p0554.md) 142 | - forum of [Study Group 14](https://groups.google.com/a/isocpp.org/forum/#!forum/sg14) 143 | 144 | ## Contact Information 145 | 146 | All feedback greatly appreciated. 147 | 148 | - [GitHub](https://github.com/johnmcfarlane) 149 | - [SG14 forum](https://groups.google.com/a/isocpp.org/forum/#!forum/sg14) 150 | - [fixed-point@john.mcfarlane.name](mailto:fixed-point@john.mcfarlane.name) 151 | -------------------------------------------------------------------------------- /src/test/p0675.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief tests of C++17-only examples listed in paper, P0675 9 | 10 | #if (__cplusplus > 201402L) 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include 19 | 20 | using sg14::_impl::identical; 21 | 22 | namespace { 23 | // example type, smart_integer, is based off of sg14::elastic_integer 24 | template 25 | struct smart_integer : public sg14::_impl::number_base, Rep> { 26 | constexpr smart_integer(const Rep& rhs) : sg14::_impl::number_base, Rep>{rhs} {} 27 | }; 28 | } 29 | 30 | namespace sg14 { 31 | namespace _impl { 32 | template 33 | constexpr auto operate(smart_integer const& lhs, smart_integer const& rhs, subtract_op) { 34 | using result_type = sg14::_impl::make_signed_t; 35 | return smart_integer(static_cast(to_rep(lhs))-static_cast(to_rep(rhs))); 36 | }; 37 | 38 | template 39 | constexpr auto operate(smart_integer const& lhs, smart_integer const& rhs, multiply_op) { 40 | using result_type = sg14::_impl::make_signed_t::value|is_signed::value>; 41 | return smart_integer(static_cast(to_rep(lhs))*static_cast(to_rep(rhs))); 42 | }; 43 | 44 | template 45 | constexpr auto operate(smart_integer const& lhs, smart_integer const& rhs, equal_op) { 46 | return to_rep(lhs)==to_rep(rhs); 47 | }; 48 | } 49 | } 50 | namespace { 51 | // example type, fixed_point, is taken directy from sg14::fixed_point 52 | using sg14::fixed_point; 53 | 54 | // example type, rounded_integer, is based off of sg14::precise_integer 55 | template 56 | using rounded_integer = sg14::precise_integer; 57 | 58 | TEST(P0675, compose_from_fundamental) { 59 | // use an unsigned 16-bit integer to approximate a real number with 2 integer and 14 fractional digits 60 | auto pi = fixed_point{3.141}; 61 | ASSERT_TRUE(pi > 3.1 && pi < 3.2); 62 | 63 | // use int to store value gained using accurate rounding mode 64 | auto num_children = rounded_integer{2.6}; 65 | ASSERT_TRUE(num_children == 3); 66 | } 67 | 68 | TEST(P0675, compose_from_components) { 69 | auto num = fixed_point, -4>{15.9375}; 70 | ASSERT_TRUE(sg14::_impl::from_rep(1) == 1. / 16); 71 | } 72 | 73 | TEST(P0675, smart_multiply) { 74 | // smart_integer chooses appropriate signedness for results of arithmetic operations 75 | auto a = smart_integer{7u}; 76 | auto b = smart_integer{-3}; 77 | auto c = a * b; // smart_integer{-21} 78 | ASSERT_TRUE(identical(smart_integer{-21}, c)); 79 | } 80 | 81 | TEST(P0675, smart_add) { 82 | auto m = smart_integer{5u}; 83 | auto s = smart_integer{10u}; 84 | auto d = m - s; // smart_integer{-5} 85 | ASSERT_TRUE(identical(smart_integer{-5}, d)); 86 | } 87 | 88 | namespace desirata { 89 | // num_digits 90 | template constexpr auto num_digits_v = sg14::digits_v; 91 | static_assert(num_digits_v == 63); 92 | 93 | // set_num_digits 94 | template using set_num_digits_t = sg14::set_digits_t; 95 | static_assert(std::is_same_v, std::uint8_t>); 96 | 97 | // is_composite 98 | using sg14::is_composite_v; 99 | static_assert(!is_composite_v); 100 | static_assert(is_composite_v>); 101 | 102 | // to_rep 103 | using sg14::to_rep; 104 | static_assert(identical(1L, to_rep()(1L))); 105 | static_assert(identical(1L, to_rep>()(smart_integer{1L}))); 106 | 107 | // from_rep 108 | using sg14::from_rep; 109 | static_assert(identical(7, from_rep()(7))); 110 | static_assert(identical(fixed_point{49.5}, from_rep>()(99))); 111 | 112 | // from_value 113 | using sg14::from_value_t; 114 | static_assert(identical(fixed_point{99}, from_value_t, unsigned long>{99UL})); 115 | } 116 | } 117 | 118 | #endif // __cplusplus > 201402L 119 | -------------------------------------------------------------------------------- /src/test/p0037.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include "sample_functions.h" 8 | 9 | #include 10 | 11 | #include 12 | 13 | using sg14::fixed_point; 14 | using sg14::make_fixed; 15 | using sg14::make_ufixed; 16 | using sg14::multiply; 17 | using sg14::set_digits_t; 18 | using sg14::sqrt; 19 | using std::is_same; 20 | using sg14::_impl::identical; 21 | 22 | //////////////////////////////////////////////////////////////////////////////// 23 | // Tests of Examples in P0037 24 | 25 | TEST(proposal, make_ufixed) 26 | { 27 | make_ufixed<4, 4> value{15.9375}; 28 | ASSERT_EQ(value, 15.9375); 29 | } 30 | 31 | TEST(proposal, make_fixed) 32 | { 33 | make_fixed<2, 29> value{3.141592653}; 34 | ASSERT_EQ(value, 3.1415926516056061); 35 | } 36 | 37 | // Operator Overloads 38 | 39 | static_assert(identical(fixed_point{8} + fixed_point{3}, fixed_point{11}), "Incorrect information in P0037 section, Operator Overloads"); 40 | static_assert(identical(fixed_point{8} + 3, fixed_point{11}), "Incorrect information in P0037 section, Operator Overloads"); 41 | static_assert(identical(fixed_point{8} + float{3}, float{11}), "Incorrect information in P0037 section, Operator Overloads"); 42 | 43 | // Overflow 44 | TEST(proposal, overflow) { 45 | switch (sizeof(int)) { 46 | case 4: { 47 | auto sum = make_ufixed<2, 30>(3) + make_ufixed<2, 30>(1); 48 | ASSERT_TRUE(sum == 0); 49 | break; 50 | } 51 | case 8: { 52 | auto sum = make_ufixed<2, 62>(3) + make_ufixed<2, 62>(1); 53 | ASSERT_TRUE(sum == 0); 54 | break; 55 | } 56 | default: 57 | FAIL() << "dying to know what architecture this is"; 58 | } 59 | } 60 | 61 | // Underflow 62 | static_assert(identical(make_fixed<7, 0>(15)/make_fixed<7, 0>(2), fixed_point(7.5f)), 63 | "Incorrect information in proposal section, Underflow"); 64 | 65 | // Named Arithmetic Functions 66 | namespace named_arithmetic1 { 67 | constexpr auto f = fixed_point{15.9375}; 68 | constexpr auto p = multiply(f, f); 69 | 70 | static_assert(identical(p, fixed_point{254.00390625}), "Incorrect information in proposal section, Named Arithmetic Functions"); 71 | } 72 | 73 | TEST(proposal, named_arithmetic2) 74 | { 75 | auto f = fixed_point{15.9375}; 76 | auto p = f * f; 77 | 78 | static_assert(is_same>::value, "Incorrect information in proposal section, Named Arithmetic Functions"); 79 | ASSERT_EQ(p, 0.); 80 | } 81 | 82 | namespace named_arithmetic3 { 83 | constexpr auto f = fixed_point{15.9375}; 84 | constexpr auto p = multiply(f, f); 85 | 86 | static_assert(identical(p, fixed_point{254.00390625}), 87 | "Incorrect information in proposal section, Named Arithmetic Functions"); 88 | } 89 | 90 | namespace named_arithmetic4 { 91 | constexpr auto a1 = fixed_point{0x7f00000000LL}; 92 | constexpr auto a2 = fixed_point{0x7f}; 93 | constexpr auto s = add(a1, a2); 94 | 95 | static_assert(identical(s, fixed_point{0x7f0000007fLL}), "Incorrect information in proposal section, Named Arithmetic Functions"); 96 | } 97 | 98 | namespace named_arithmetic5 { 99 | constexpr auto n = fixed_point{1}; 100 | constexpr auto d = fixed_point{2}; 101 | 102 | constexpr auto q1 = n/d; 103 | static_assert(identical(q1, fixed_point{0.5}), "Incorrect information in proposal section, Named Arithmetic Functions"); 104 | 105 | constexpr auto q2 = divide(n, d); 106 | static_assert(identical(q2, fixed_point{0}), "Incorrect information in proposal section, Named Arithmetic Functions"); 107 | } 108 | 109 | // The `width` Helper Type 110 | static_assert(sg14::digits::value == 15, "Incorrect information in proposal section, The `width` Helper Type"); 111 | 112 | // The `set_width` and `set_digits_t` Helper Types 113 | static_assert(is_same, int64_t>::value, "Incorrect information in proposal section, The `set_width` and `set_digits_t` Helper Types"); 114 | 115 | // Examples 116 | template 117 | constexpr auto magnitude(Fp x, Fp y, Fp z) 118 | -> decltype(sqrt(x*x+y*y+z*z)) 119 | { 120 | return sqrt(x*x+y*y+z*z); 121 | } 122 | 123 | TEST(proposal, examples) 124 | { 125 | constexpr auto m = magnitude( 126 | make_ufixed<4, 12>(1), 127 | make_ufixed<4, 12>(4), 128 | make_ufixed<4, 12>(9)); 129 | static_assert(identical(m, make_fixed<7, 24>{9.8994948864}), "Incorrect information in proposal section, Examples"); 130 | } 131 | 132 | TEST(proposal, zero) 133 | { 134 | static fixed_point<> zero; 135 | ASSERT_EQ(zero, fixed_point<>(0)); 136 | } 137 | -------------------------------------------------------------------------------- /src/benchmark/review.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from argparse import ArgumentParser 4 | from io import StringIO 5 | from itertools import chain 6 | from os import chdir, curdir, path 7 | from subprocess import CalledProcessError, check_output, PIPE 8 | from sys import argv, stderr 9 | 10 | from report import benchmarks_from_buffer, csv_from_report, table_from_benchmarks 11 | 12 | 13 | def run_command(dir, command): 14 | chdir(dir) 15 | return check_output(command, stderr=PIPE).decode('ascii') 16 | 17 | def run_from_repo(args, command): 18 | return run_command(args.repo, command) 19 | 20 | def run_from_build(args, command): 21 | return run_command(args.build, command) 22 | 23 | 24 | def run_benchmarks(args): 25 | # configure 26 | run_from_build(args, ["cmake", args.repo, "-DCMAKE_BUILD_TYPE=Release"]) 27 | 28 | # build 29 | run_from_build(args, ["make", "fp_benchmark", "-j", str(args.jobs or 1)]) 30 | 31 | # benchmark 32 | benchmark_args = [ 33 | "--benchmark_format=csv", 34 | "--benchmark_filter="+args.filter 35 | ] 36 | buffer = run_from_build(args, ["./fp_benchmark"] + benchmark_args) 37 | 38 | # clean 39 | run_from_build(args, ["make", "clean"]) 40 | 41 | return buffer 42 | 43 | def extract_names(collection): 44 | return sorted(set(chain.from_iterable(benchmarks for commit, benchmarks in collection))) 45 | 46 | def make_row(commit, benchmarks, names): 47 | return [commit] + [benchmarks.get(name, '-') for name in names] 48 | 49 | 50 | # get a list of the commit SHAs in ascending chronological order 51 | def get_commits(args): 52 | watch_files = ["--", "include", "src/benchmarks/*", "CMakeLists.txt", "*.cmake"] 53 | git_log_args = (["--merges"] if args.merges else []) + (["--no-merges"] if args.no_merges else []) + [ 54 | "--all", 55 | "--date-order", 56 | "--reverse", 57 | "--format=format:%H", 58 | args.range or "--all" 59 | ] + (["-{}".format(args.max_commits)] if args.max_commits else []) + ([] if args.merges else watch_files) 60 | return run_from_repo(args, ["git", "log"] + git_log_args).split('\n') 61 | 62 | def benchmark(args, commit, commits): 63 | # print progress indicator 64 | stderr.write("\r{}/{} {}".format(commits.index(commit), len(commits), commit)) 65 | stderr.flush() 66 | 67 | # checkout the given commit 68 | run_from_repo(args, ["git", "checkout", "--force", commit]) 69 | 70 | try: 71 | # run the benchmarks and store results as a stream 72 | stream = StringIO(run_benchmarks(args)) 73 | except CalledProcessError as e: 74 | # failure likely means a commit from before the benchmarks existed 75 | return {} 76 | 77 | # decode stream as CSV table 78 | benchmarks = benchmarks_from_buffer(stream) 79 | 80 | # refine table 81 | report = table_from_benchmarks(benchmarks) 82 | 83 | # determine the columns to extract 84 | title_row = report[0] 85 | name_index = title_row.index('name') 86 | cpu_time_index = title_row.index('cpu_time') 87 | 88 | # return list of tuples of benchmark test name to result 89 | return {cell[name_index]: cell[cpu_time_index] for cell in report[1:]} 90 | 91 | def collect(args, commits): 92 | return [(commit, benchmark(args, commit, commits)) for commit in commits] 93 | 94 | def collate(collection): 95 | names = extract_names(collection) 96 | return [["commit"] + names] + [make_row(commit, benchmarks, names) for commit, benchmarks in collection if benchmarks] 97 | 98 | 99 | def main(args): 100 | commits = get_commits(args) 101 | collection = collect(args, commits) 102 | table = collate(collection) 103 | return csv_from_report(table) 104 | 105 | 106 | if __name__ == "__main__": 107 | parser = ArgumentParser(description="For each commit (chronologically) of the repository, run the benchmarking target and chart results as a table of benchmarks by commit.") 108 | parser.add_argument("repo", help="path to the fixed_point repository - preferably a different copy than the one from which this script is run") 109 | parser.add_argument("--build", help="path to cmake build folder; defaults to current folder", default=curdir) 110 | parser.add_argument("--range", help="range or revisions to visit") 111 | parser.add_argument("--filter", help="filters benchmarks based on regex pattern", default=".*") 112 | parser.add_argument("--merges", help="visit only merge commits", type=bool, default=False) 113 | parser.add_argument("--no-merges", help="skip merge commits", type=bool, default=False) 114 | parser.add_argument("--max_commits", help="maximum number of commits to test (going back from most recent)", type=int) 115 | parser.add_argument("-j", "--jobs", help="number of parallel build jobs", type=int, default=1) 116 | 117 | args = parser.parse_args() 118 | args.repo = path.abspath(args.repo) 119 | args.build = path.abspath(args.build) 120 | 121 | print(main(args)) 122 | -------------------------------------------------------------------------------- /include/sg14/auxiliary/elastic_fixed_point.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief essential definitions related to the `sg14::elastic_fixed_point` type 9 | 10 | #if !defined(SG14_ELASTIC_FIXED_POINT_H) 11 | #define SG14_ELASTIC_FIXED_POINT_H 1 12 | 13 | #include "elastic_integer.h" 14 | #include 15 | 16 | /// study group 14 of the C++ working group 17 | namespace sg14 { 18 | //////////////////////////////////////////////////////////////////////////////// 19 | //////////////////////////////////////////////////////////////////////////////// 20 | // sg14-specific definitions 21 | 22 | /// \brief literal real number approximation that uses fixed-point arithmetic and auto-widens to avoid overflow 23 | /// 24 | /// \tparam IntegerDigits the number of integer digits that can be stored 25 | /// \tparam FractionalDigits the number of fractional digits that can be stored 26 | /// \tparam Narrowest the most narrow integer type to use to represent values 27 | /// 28 | /// \sa elastic_integer 29 | 30 | template 31 | using elastic_fixed_point = fixed_point, -FractionalDigits>; 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | //////////////////////////////////////////////////////////////////////////////// 35 | // sg14::make_elastic_fixed_point 36 | 37 | /// \brief generate an \ref sg14::elastic_fixed_point object of given value 38 | /// 39 | /// \tparam Narrowest the narrowest type to use as storage 40 | /// in the resultant \ref sg14::elastic_fixed_point object 41 | /// \tparam Integral the type of Value 42 | /// \tparam Value the integer number to be represented 43 | /// 44 | /// \return the given value to be represented using an \ref sg14::elastic_fixed_point type 45 | /// 46 | /// \note The return type is guaranteed to be no larger than is necessary to represent the value. 47 | /// 48 | /// \par Example 49 | /// 50 | /// To define an int-sized object using \ref make_elastic_fixed_point and \ref const_integer: 51 | /// \snippet snippets.cpp define an int-sized object using make_elastic_fixed_point and const_integer 52 | 53 | template< 54 | typename Narrowest = int, 55 | typename Integral = int, 56 | Integral Value = 0> 57 | constexpr elastic_fixed_point<_impl::max(_impl::used_bits_symmetric(Value), 1), -trailing_bits(Value), Narrowest> 58 | make_elastic_fixed_point(const_integer = const_integer{}) 59 | { 60 | return Value; 61 | } 62 | 63 | //////////////////////////////////////////////////////////////////////////////// 64 | //////////////////////////////////////////////////////////////////////////////// 65 | // sg14::make_elastic_fixed_point 66 | 67 | /// 68 | /// \tparam Narrowest the most narrow storage type of the resultant \ref sg14::elastic_fixed_point object 69 | /// \tparam Integral the type of \ref value 70 | /// 71 | /// \note The return type is guaranteed to be no larger than is necessary to represent the value. 72 | /// 73 | /// \par Example 74 | /// 75 | /// To define a byte-sized object using make_elastic_fixed_point and _c: 76 | /// \snippet snippets.cpp define a byte-sized object using \ref make_elastic_fixed_point and \ref _c 77 | 78 | /// \brief generate an \ref sg14::elastic_fixed_point object of given value 79 | template 80 | constexpr elastic_fixed_point::digits, 0, Narrowest> 81 | make_elastic_fixed_point(Integral value) 82 | { 83 | return {value}; 84 | } 85 | 86 | //////////////////////////////////////////////////////////////////////////////// 87 | //////////////////////////////////////////////////////////////////////////////// 88 | // sg14::literals::operator "" _elastic 89 | 90 | /// \brief generate an \ref sg14::elastic_fixed_point object using a literal 91 | /// 92 | /// \tparam Digits the characters of the literal sequence 93 | /// 94 | /// \return the given value to be represented using an \ref sg14::elastic_fixed_point type 95 | /// 96 | /// \note The return type is guaranteed to be no larger 97 | /// than is necessary to represent the maximum value of Integral. 98 | /// 99 | /// \par Example 100 | /// 101 | /// To define an int-sized object with value 1536: 102 | /// \snippet snippets.cpp define an object using elastic literal 103 | 104 | namespace literals { 105 | template 106 | constexpr auto operator "" _elastic() 107 | -> decltype(make_elastic_fixed_point::value>()) { 108 | return make_elastic_fixed_point::value>(); 109 | } 110 | } 111 | } 112 | 113 | #endif // SG14_ELASTIC_FIXED_POINT_H 114 | -------------------------------------------------------------------------------- /include/sg14/auxiliary/numeric.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief functions that might belong in the header 9 | 10 | #ifndef SG14_NUMERIC_H 11 | #define SG14_NUMERIC_H 12 | 13 | #include 14 | 15 | #include 16 | 17 | /// study group 14 of the C++ working group 18 | namespace sg14 { 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | // sg14::trailing_bits 22 | 23 | namespace _numeric_impl { 24 | template 25 | constexpr int trailing_bits_positive(Integer value, int mask_bits = sizeof(Integer)*CHAR_BIT/2) 26 | { 27 | return ((value & ((Integer{1} << mask_bits)-1))==0) 28 | ? mask_bits+trailing_bits_positive(value/(Integer{1} << mask_bits), mask_bits) 29 | : (mask_bits>1) 30 | ? trailing_bits_positive(value, mask_bits/2) 31 | : 0; 32 | } 33 | 34 | template 35 | struct trailing_bits { 36 | static constexpr int f(Integer value) 37 | { 38 | return value ? trailing_bits_positive(value) : 0; 39 | } 40 | }; 41 | 42 | template 43 | struct trailing_bits::is_signed>> { 44 | static constexpr int f(Integer value) 45 | { 46 | // Most negative number is not exploited; 47 | // thus negating the result or subtracting it from something else 48 | // will less likely result in overflow. 49 | return (value>0) 50 | ? trailing_bits_positive(value) 51 | : (value<0) 52 | ? trailing_bits_positive(-value) 53 | : 0; 54 | } 55 | }; 56 | } 57 | 58 | template 59 | constexpr int trailing_bits(Integer value) 60 | { 61 | return _numeric_impl::trailing_bits::f(value); 62 | } 63 | 64 | //////////////////////////////////////////////////////////////////////////////// 65 | // sg14::used_bits 66 | 67 | namespace _numeric_impl { 68 | template 69 | constexpr int used_bits_positive(Integer value, int mask_bits = sizeof(Integer)*CHAR_BIT/2) 70 | { 71 | return (value>=(Integer{1} << mask_bits)) 72 | ? mask_bits+used_bits_positive(value/(Integer{1} << mask_bits), mask_bits) 73 | : (mask_bits>1) 74 | ? used_bits_positive(value, mask_bits/2) 75 | : 1; 76 | } 77 | } 78 | 79 | namespace _impl { 80 | template 81 | constexpr int used_bits_symmetric(Integer value) 82 | { 83 | // Most negative number is not exploited; 84 | // thus negating the result or subtracting it from something else 85 | // will less likely result in overflow. 86 | return (value>0) 87 | ? _numeric_impl::used_bits_positive(value) 88 | : (value<0) 89 | #if defined(_MSC_VER) 90 | #pragma warning(push) 91 | #pragma warning(disable: 4146) 92 | #endif 93 | ? _numeric_impl::used_bits_positive(-value) 94 | #if defined(_MSC_VER) 95 | #pragma warning(pop) 96 | #endif 97 | : 0; 98 | } 99 | } 100 | 101 | namespace _numeric_impl { 102 | struct used_bits { 103 | template 104 | constexpr _impl::enable_if_t::is_signed, int> operator()(Integer value) const 105 | { 106 | return value ? used_bits_positive(value) : 0; 107 | } 108 | 109 | template::is_signed, int>> 110 | constexpr int operator()(Integer value) const 111 | { 112 | // Most negative number is not exploited; 113 | // thus negating the result or subtracting it from something else 114 | // will less likely result in overflow. 115 | return (value>0) 116 | ? used_bits_positive(value) 117 | : (value==0) 118 | ? 0 119 | : used_bits()(Integer(-1)-value); 120 | } 121 | }; 122 | } 123 | 124 | template 125 | constexpr int used_bits(Integer value) 126 | { 127 | return for_rep(_numeric_impl::used_bits(), value); 128 | } 129 | 130 | //////////////////////////////////////////////////////////////////////////////// 131 | // sg14::leading_bits 132 | 133 | #if !defined(_MSC_VER) && !defined(SG14_DISABLE_GCC_BUILTINS) 134 | constexpr int leading_bits(int value) 135 | { 136 | return (value>0) 137 | ? __builtin_clz(value)-1 138 | : digits::value-used_bits(value); 139 | } 140 | #endif 141 | 142 | template 143 | constexpr int leading_bits(const Integer& value) 144 | { 145 | return digits::value-used_bits(value); 146 | } 147 | } 148 | 149 | #endif // SG14_NUMERIC_H 150 | -------------------------------------------------------------------------------- /src/test/p0381.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #include 10 | 11 | #if defined(SG14_BOOST_ENABLED) 12 | #include 13 | #endif 14 | 15 | using std::declval; 16 | using std::is_same; 17 | 18 | namespace sample1 { 19 | // range of a*b is UCHAR_MAX*UCHAR_MAX but range of return value is UCHAR_MAX 20 | uint8_t multiply(uint8_t a, uint8_t b) 21 | { 22 | return a*b; 23 | } 24 | 25 | // sample 1 tests 26 | using single_width = uint8_t; 27 | 28 | static_assert(UCHAR_MAX==255, "incorrect assumption about value of UCHAR_MAX"); 29 | static_assert(UCHAR_MAX*UCHAR_MAX==65025, "incorrect assumption about value of UCHAR_MAX"); 30 | 31 | TEST(p0381, multiply_uint8_ok) 32 | { 33 | ASSERT_EQ(100, multiply(10, 10)); 34 | } 35 | 36 | TEST(p0381, multiply_uint8_overflow) 37 | { 38 | ASSERT_NE(400, multiply(20, 20)); 39 | } 40 | } 41 | 42 | #if (__cplusplus>=201402L) 43 | namespace sample2 { 44 | // range of a*b is UINT_MAX*UINT_MAX but range of return value is UINT_MAX 45 | auto multiply(unsigned a, unsigned b) 46 | { 47 | return a*b; 48 | } 49 | 50 | // sample 2 tests 51 | using wide_type = unsigned long long; 52 | 53 | static_assert(sizeof(wide_type)>=sizeof(unsigned)*2, 54 | "the following tests assume unsigned long long is twice the size of unsigned"); 55 | static_assert(is_same()*declval()), unsigned>::value, 56 | "incorrect assumption about type of result of unsigned * unsigned"); 57 | 58 | TEST(p0381, multiply_unsigned_ok) 59 | { 60 | ASSERT_EQ(400u, multiply(20u, 20u)); 61 | } 62 | 63 | TEST(p0381, multiply_unsigned_overflow) 64 | { 65 | ASSERT_NE(static_cast(UINT_MAX)*static_cast(UINT_MAX), 66 | static_cast(multiply(UINT_MAX, UINT_MAX))); 67 | } 68 | } 69 | 70 | namespace sample3 { 71 | auto multiply(uint32_t a, uint32_t b) 72 | { 73 | using result_type = uint64_t; 74 | return result_type{a}*result_type{b}; 75 | } 76 | 77 | // sample 3 tests 78 | static_assert(is_same(), declval()))>::value, 79 | "incorrect assumption about result of multiply function"); 80 | 81 | TEST(p0381, multiply_unsigned_ok) 82 | { 83 | ASSERT_EQ(400u, multiply(20u, 20u)); 84 | } 85 | 86 | TEST(p0381, multiply_unsigned_still_ok) 87 | { 88 | ASSERT_EQ(static_cast(UINT_MAX)*static_cast(UINT_MAX), 89 | static_cast(multiply(UINT_MAX, UINT_MAX))); 90 | } 91 | } 92 | 93 | namespace sample4 { 94 | // Sample 4 intentionally does not exist. If it did, there would be no need for P0381! 95 | } 96 | 97 | #if defined(SG14_BOOST_ENABLED) 98 | namespace sample5 { 99 | template 100 | auto multiply(Operand a, Operand b) 101 | { 102 | constexpr auto operand_width = sizeof(Operand)*CHAR_BIT*2; 103 | using result_type = typename boost::uint_t::fast; 104 | return result_type{a}*result_type{b}; 105 | } 106 | 107 | // sample 5 tests are a lot like sample 3 tests 108 | 109 | // they are more generic 110 | #if ! defined(__APPLE__) // uint64_t is a different type depending on the version of XCode 111 | static_assert(is_same(), declval()))>::value, 112 | "incorrect assumption about result of multiply function"); 113 | #endif 114 | static_assert(is_same(), declval()))>::value, 115 | "incorrect assumption about result of multiply function"); 116 | 117 | // but don't do so well with signed types 118 | // static_assert(is_same(), declval()))>::value, 119 | // "incorrect assumption about result of multiply function"); 120 | // static_assert(is_same(), declval()))>::value, 121 | // "incorrect assumption about result of multiply function"); 122 | 123 | TEST(p0381, multiply_unsigned_ok) 124 | { 125 | ASSERT_EQ(400u, multiply(20u, 20u)); 126 | } 127 | 128 | TEST(p0381, multiply_unsigned_still_ok) 129 | { 130 | ASSERT_EQ(static_cast(UINT_MAX)*static_cast(UINT_MAX), 131 | static_cast(multiply(UINT_MAX, UINT_MAX))); 132 | } 133 | } 134 | #endif // defined(SG14_BOOST_ENABLED) 135 | #endif // C++14 136 | 137 | namespace determining { 138 | using sg14::digits; 139 | 140 | static_assert(digits::value == 16, "the digits of uint16_t is exactly 16 bits"); 141 | static_assert(digits::value >= 63, "long long has a digits of at least 64 bits"); 142 | static_assert(digits::value >= digits::value, "short is no longer than long"); 143 | static_assert(digits::value >= digits::value, "a wide character is at least as wide as a character"); 144 | } 145 | 146 | namespace specifiying { 147 | using sg14::set_digits_t; 148 | using sg14::digits; 149 | 150 | static_assert(is_same, int8_t>::value, "int8_t is a signed 8-bit integer"); 151 | static_assert(is_same, uint32_t>::value, "uint32_t is an unsigned 32-bit integer"); 152 | static_assert(is_same, uint16_t>::value, "a 64-bit unsigned integer was narrowed to 16-bits"); 153 | static_assert(is_same, int64_t>::value || is_same, uint64_t>::value, "char may or may not be signed so the result may be uint64_t or int64_t"); 154 | static_assert(digits>::value >= 10, "result must be at least 10 bits wide"); 155 | } 156 | -------------------------------------------------------------------------------- /src/test/boost.simd.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #if defined(SG14_BOOST_SIMD_ENABLED) 8 | 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | namespace { 19 | using sg14::fixed_point; 20 | using boost::simd::pack; 21 | 22 | template 23 | using fpp = fixed_point, Exponent>; 24 | 25 | template 26 | struct rebind_s; 27 | 28 | template 29 | struct rebind_s> { 30 | using type = pack; 31 | }; 32 | 33 | template 34 | using rebind = typename rebind_s::type; 35 | 36 | template 37 | using initializer = rebind; 38 | } 39 | 40 | namespace sg14 { 41 | template 42 | bool operator==(const fpp &lhs, const fpp &rhs) noexcept { 43 | return boost::simd::compare_equal(lhs.data(), rhs.data()); 44 | } 45 | 46 | template 47 | bool operator!=(const fpp &lhs, const fpp &rhs) noexcept { 48 | return !(lhs==rhs); 49 | } 50 | } 51 | 52 | namespace { 53 | namespace test_numeric_limits { 54 | static_assert(std::numeric_limits>::is_integer, ""); 55 | static_assert(!std::numeric_limits>::is_iec559, ""); 56 | 57 | static_assert(!std::numeric_limits>::is_integer, ""); 58 | static_assert(std::numeric_limits>::is_iec559, ""); 59 | } 60 | 61 | namespace test_set_digits { 62 | using actual_type = sg14::set_digits_t, 15>; 63 | using expected_type = boost::simd::pack; 64 | static_assert(std::is_same::value, ""); 65 | } 66 | 67 | namespace test_set_make_unsigned { 68 | using actual_type = sg14::make_unsigned_t>; 69 | using expected_type = boost::simd::pack; 70 | static_assert(std::is_same::value, ""); 71 | } 72 | 73 | namespace test_set_make_signed { 74 | using actual_type = sg14::make_signed_t>; 75 | using expected_type = boost::simd::pack; 76 | static_assert(std::is_same::value, ""); 77 | } 78 | 79 | TEST(boost_simd, scale) { 80 | using pack = boost::simd::pack; 81 | auto input = pack{65535, 0}; 82 | auto output = sg14::scale()(input, 2, 5); 83 | auto expected = pack{65535*32, 0}; 84 | ASSERT_TRUE(boost::simd::compare_equal(expected, output)); 85 | } 86 | 87 | TEST(boost_simd, shift_left) { 88 | using output_type = boost::simd::pack; 89 | using input_type = boost::simd::pack; 90 | auto input = input_type{65535, 0}; 91 | auto output = sg14::_impl::shift_left<5, output_type>(input); 92 | auto expected = output_type{65535*32, 0}; 93 | ASSERT_TRUE(boost::simd::compare_equal(expected, output)); 94 | } 95 | 96 | TEST(boost_simd, equality) { 97 | using fpp = fpp; 98 | using init = initializer; 99 | auto lhs = fpp(init(6.25, -8., 0., 1.625)); 100 | auto rhs = fpp(init(6.25, -8., 0., 1.625)); 101 | ASSERT_EQ(lhs, rhs); 102 | } 103 | 104 | TEST(boost_simd, inequality) { 105 | using fpp = fpp; 106 | using init = initializer; 107 | auto lhs = fpp{init{8237416.17618, 3278928.9012393}}; 108 | auto rhs = fpp{init{8237416.17618, 4278928.9012393}}; 109 | ASSERT_NE(lhs, rhs); 110 | } 111 | 112 | TEST(boost_simd, conversion) { 113 | using wide_type = pack; 114 | using narrow_type = pack; 115 | auto w = wide_type{pack{INT64_C(5000000000), INT64_C(-42000000000000)}}; 116 | auto n = narrow_type{pack{INT16_C(32000), INT64_C(-7)}}; 117 | 118 | w = static_cast(n); // requires new explicit operator 119 | n = static_cast(w); // requires new explicit conversion operator 120 | //w = n; // doesn't compile with or without explicit conversion operator 121 | //n = w; // doesn't compile with or without explicit conversion operator 122 | 123 | auto narrow_implicit(w); // happens already 124 | auto wide_implicit(n); // happens already 125 | auto narrow_explicit{w}; // happens already 126 | auto wide_explicit{n}; // happens already 127 | } 128 | 129 | TEST(boost_simd, multiply) { 130 | using operand_type = fpp; 131 | using result_type = fpp; 132 | using initializer = initializer; 133 | 134 | auto expected = result_type{initializer{-7.9375, -1.}}; 135 | auto multiplier = operand_type{initializer{7.9375, -8.}}; 136 | auto multiplicand = operand_type{initializer{-1, .125}}; 137 | auto product = multiplier * multiplicand; 138 | ASSERT_EQ(expected, product); 139 | } 140 | 141 | TEST(boost_simd, add) { 142 | using operand_type = fpp; 143 | using result_type = operand_type; 144 | using initializer = initializer; 145 | 146 | auto expected = result_type{initializer{7.9375+-1, -8.+.125, 0+-5, 3.5+-3.5}}; 147 | auto augend = operand_type{initializer{7.9375, -8., 0, 3.5}}; 148 | auto addend = operand_type{initializer{-1, .125, -5, -3.5}}; 149 | auto sum = augend + addend; 150 | ASSERT_EQ(expected, sum); 151 | } 152 | } 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /include/sg14/auxiliary/precise_integer.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #if !defined(SG14_PRECISE_INTEGER_H) 8 | #define SG14_PRECISE_INTEGER_H 1 9 | 10 | #include 11 | #include 12 | 13 | namespace sg14 { 14 | 15 | struct closest_rounding_tag { 16 | template 17 | static constexpr To convert(const From& from) 18 | { 19 | return static_cast(std::intmax_t(from+((from>=0) ? .5 : -.5))); 20 | } 21 | }; 22 | 23 | template 24 | class precise_integer : public _impl::number_base, Rep> { 25 | using super = _impl::number_base, Rep>; 26 | public: 27 | using rounding = RoundingTag; 28 | 29 | constexpr precise_integer() = default; 30 | 31 | template::is_integer, int> Dummy = 0> 32 | constexpr precise_integer(const T& v) 33 | : super(static_cast(v)) { } 34 | 35 | template::is_integer, int> Dummy = 0> 36 | constexpr precise_integer(const T& v) 37 | : super(rounding::template convert(v)) { } 38 | 39 | template 40 | constexpr explicit operator T() const 41 | { 42 | return static_cast(super::data()); 43 | } 44 | }; 45 | 46 | //////////////////////////////////////////////////////////////////////////////// 47 | // numeric type traits 48 | 49 | template 50 | struct digits> : digits { 51 | }; 52 | 53 | template 54 | struct set_digits, MinNumBits> { 55 | using type = precise_integer, RoundingTag>; 56 | }; 57 | 58 | namespace _impl { 59 | template 60 | struct get_rep> { 61 | using type = Rep; 62 | }; 63 | 64 | template 65 | struct set_rep, NewRep> { 66 | using type = precise_integer; 67 | }; 68 | } 69 | 70 | template 71 | struct from_value, Value> { 72 | using type = precise_integer; 73 | }; 74 | 75 | template 76 | struct scale> 77 | : scale<_impl::number_base, Rep>> { 78 | }; 79 | 80 | namespace _precise_integer_impl { 81 | //////////////////////////////////////////////////////////////////////////////// 82 | // comparison operators 83 | 84 | template 85 | struct is_precise_integer : std::false_type { 86 | }; 87 | 88 | template 89 | struct is_precise_integer> : std::true_type { 90 | }; 91 | } 92 | 93 | //////////////////////////////////////////////////////////////////////////////// 94 | // binary arithmetic 95 | 96 | namespace _impl { 97 | // for operands with a common tag 98 | template> 99 | constexpr auto operate_common_tag( 100 | const precise_integer& lhs, 101 | const precise_integer& rhs) 102 | -> decltype(from_rep, RoundingTag>>(Operator()(lhs.data(), rhs.data()))) 103 | { 104 | using result_type = precise_integer, RoundingTag>; 105 | return from_rep(Operator()(lhs.data(), rhs.data())); 106 | } 107 | 108 | // for arithmetic operands with different policies 109 | template = 0> 110 | constexpr auto operate_common_tag( 111 | const precise_integer& lhs, 112 | const precise_integer& rhs) 113 | -> decltype(Operator()(lhs.data(), rhs.data())) 114 | { 115 | return Operator()(lhs.data(), rhs.data()); 116 | } 117 | 118 | // for arithmetic operands with different policies 119 | template 120 | constexpr auto operate( 121 | const precise_integer& lhs, 122 | const precise_integer& rhs, 123 | Operator) 124 | -> decltype(operate_common_tag>(lhs, rhs)) 125 | { 126 | return operate_common_tag>(lhs, rhs); 127 | } 128 | } 129 | 130 | //////////////////////////////////////////////////////////////////////////////// 131 | // binary bitwise 132 | 133 | template 134 | constexpr auto operator<<( 135 | const precise_integer& lhs, 136 | const RhsInteger& rhs) 137 | -> decltype(from_rep>(_impl::to_rep(lhs) << rhs)) 138 | { 139 | return from_rep>(_impl::to_rep(lhs) << rhs); 142 | } 143 | } 144 | 145 | namespace std { 146 | //////////////////////////////////////////////////////////////////////////////// 147 | // std::numeric_limits specialization for precise_integer 148 | 149 | template 150 | struct numeric_limits> 151 | : numeric_limits, Rep>> {}; 152 | } 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /src/test/safe_elastic_integer.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | #include "number_test.h" 11 | 12 | namespace sg14 { 13 | // safe elastic integer 14 | template< 15 | int IntegerDigits, 16 | class OverflowTag = throwing_overflow_tag, 17 | class Narrowest = int> 18 | using safe_elastic_integer = safe_integer< 19 | elastic_integer< 20 | IntegerDigits, 21 | Narrowest>, 22 | OverflowTag>; 23 | 24 | template< 25 | class OverflowTag = throwing_overflow_tag, 26 | class Narrowest = int, 27 | class Input = int> 28 | safe_elastic_integer< 29 | std::numeric_limits::digits, 30 | OverflowTag, 31 | Narrowest> 32 | constexpr make_safe_elastic(Input const& input) 33 | { 34 | return input; 35 | } 36 | } 37 | 38 | namespace { 39 | using sg14::safe_elastic_integer; 40 | using std::is_same; 41 | using sg14::_impl::identical; 42 | 43 | namespace default_parameters { 44 | static_assert( 45 | is_same::rep::rep, int>::value, 46 | "sg14::safe_elastic_integer parameter default test failed"); 47 | } 48 | 49 | namespace test_numeric_limits { 50 | using safe_saturating_integer_2 = sg14::safe_integer, sg14::saturated_overflow_tag>; 51 | static_assert(identical( 52 | std::numeric_limits::lowest(), 53 | safe_saturating_integer_2{-3}), ""); 54 | static_assert(identical( 55 | std::numeric_limits::max(), 56 | safe_saturating_integer_2{3}), ""); 57 | static_assert(std::numeric_limits::lowest() < std::numeric_limits::max(), ""); 58 | } 59 | 60 | namespace test_comparison { 61 | static_assert(identical( 62 | sg14::convert>(sg14::throwing_overflow, 0), 63 | sg14::elastic_integer<10>{0}), ""); 64 | static_assert(safe_elastic_integer<10>(0b1010101010)==safe_elastic_integer<10>(0b1010101010), ""); 65 | } 66 | 67 | namespace test_make_safe_elastic { 68 | static_assert(identical(sg14::make_safe_elastic(std::int16_t{7}), safe_elastic_integer<15>{7}), ""); 69 | static_assert(identical(safe_elastic_integer<34>{0}, safe_elastic_integer<34>{0}), ""); 70 | } 71 | 72 | namespace test_add { 73 | static_assert( 74 | identical( 75 | safe_elastic_integer<2>{2}, 76 | safe_elastic_integer<1>{1}+safe_elastic_integer<1>{1}), 77 | "safe_elastic_integer operator+"); 78 | } 79 | 80 | namespace test_add { 81 | static_assert( 82 | identical( 83 | safe_elastic_integer<2>{2}-safe_elastic_integer<2>{1}, 84 | safe_elastic_integer<3>{1}), 85 | "safe_elastic_integer operator+"); 86 | } 87 | 88 | namespace test_multiply { 89 | static_assert(identical(safe_elastic_integer<6>{55}*safe_elastic_integer<6>{4}, safe_elastic_integer<12>{220}), "safe_elastic_integer operator*"); 90 | static_assert(identical(safe_elastic_integer<3>{7}*safe_elastic_integer<4>{10}, safe_elastic_integer<7>{70}), "safe_elastic_integer operator*"); 91 | #if defined(__clang__) || ! defined(__GNUG__) 92 | static_assert(identical(safe_elastic_integer<3>{3}*.25, .75), "safe_elastic_integer operator*"); 93 | #endif 94 | } 95 | 96 | namespace test_scale { 97 | static_assert(identical(safe_elastic_integer<3>{7}*safe_elastic_integer<4>{10}, safe_elastic_integer<7>{70}), "safe_elastic_integer operator*"); 98 | } 99 | 100 | namespace test_is_composite { 101 | using sg14::is_composite; 102 | 103 | static_assert(is_composite>::value, "is_composite> test failed"); 104 | } 105 | 106 | namespace test_digits { 107 | using sg14::digits; 108 | using sg14::set_digits_t; 109 | 110 | static_assert(digits>::value>=3, "sg14::digits / sg14::set_digits test failed"); 111 | static_assert(identical(set_digits_t, 3>{6}, safe_elastic_integer<3>{6}), "sg14::digits / sg14::set_digits test failed"); 112 | } 113 | 114 | namespace test_used_bits { 115 | using sg14::used_bits; 116 | using sg14::throwing_overflow_tag; 117 | 118 | static_assert(used_bits(safe_elastic_integer<1, throwing_overflow_tag, char>{0}) == 0, "used_bits(safe_elastic_integer)"); 119 | static_assert(used_bits(safe_elastic_integer<22, throwing_overflow_tag>{77}) == 7, "used_bits(safe_elastic_integer)"); 120 | } 121 | 122 | namespace test_leading_bits { 123 | using sg14::leading_bits; 124 | using sg14::throwing_overflow_tag; 125 | 126 | static_assert(leading_bits(safe_elastic_integer<1, throwing_overflow_tag, char>{0}) == 1, "leading_bits(safe_elastic_integer)"); 127 | static_assert(leading_bits(safe_elastic_integer<22, throwing_overflow_tag>{77}) == 15, "leading_bits(safe_elastic_integer)"); 128 | } 129 | } 130 | 131 | // given a rounding tag, invokes number_test_suite for precise_integers of all built-in types 132 | template 133 | struct test_safe_elastic_integer { 134 | template 135 | using test_subject = safe_elastic_integer; 136 | 137 | constexpr static number_test_by_rep instance{}; 138 | }; 139 | 140 | template struct test_safe_elastic_integer<1, sg14::throwing_overflow_tag>; 141 | template struct test_safe_elastic_integer<5, sg14::throwing_overflow_tag>; 142 | template struct test_safe_elastic_integer<21, sg14::throwing_overflow_tag>; 143 | 144 | #if defined(__clang__) || ! defined(__GNUG__) || (__GNUG__ > 6) 145 | template struct test_safe_elastic_integer<2, sg14::saturated_overflow_tag>; 146 | template struct test_safe_elastic_integer<8, sg14::saturated_overflow_tag>; 147 | template struct test_safe_elastic_integer<34, sg14::saturated_overflow_tag>; 148 | #endif 149 | 150 | template struct test_safe_elastic_integer<3, sg14::native_overflow_tag>; 151 | template struct test_safe_elastic_integer<13, sg14::native_overflow_tag>; 152 | template struct test_safe_elastic_integer<55, sg14::native_overflow_tag>; 153 | -------------------------------------------------------------------------------- /src/test/precise_integer.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2017. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #include "number_test.h" 10 | 11 | #include 12 | 13 | namespace { 14 | using sg14::precise_integer; 15 | using std::is_same; 16 | using sg14::_impl::identical; 17 | 18 | namespace number_base_traits { 19 | using sg14::_impl::number_base; 20 | using sg14::_impl::is_derived_from_number_base; 21 | 22 | static_assert(!is_derived_from_number_base::value, ""); 23 | static_assert(!is_derived_from_number_base, int>>::value, ""); 24 | static_assert(is_derived_from_number_base>::value, ""); 25 | } 26 | 27 | namespace default_parameters { 28 | using sg14::closest_rounding_tag; 29 | 30 | using default_rep = int; 31 | 32 | template 33 | using default_tag = sg14::closest_rounding_tag; 34 | 35 | static_assert(is_same, precise_integer>>::value, "sg14::precise_integer parameter default test failed"); 36 | 37 | static_assert(is_same::rep, default_rep>::value, "sg14::precise_integer parameter default test failed"); 38 | static_assert(is_same::rounding, default_tag>::value, "sg14::precise_integer parameter default test failed"); 39 | } 40 | 41 | namespace is_number { 42 | using sg14::_impl::is_derived_from_number_base; 43 | 44 | static_assert(is_derived_from_number_base>::value, "is_derived_from_number_base>"); 45 | } 46 | 47 | namespace test_traits { 48 | 49 | namespace test_make_signed_t { 50 | using sg14::make_signed_t; 51 | 52 | static_assert(std::is_same, make_signed_t>>::value, 53 | "sg14::make_signed_t>"); 54 | static_assert(std::is_same, make_signed_t>>::value, 55 | "sg14::make_signed_t>"); 56 | } 57 | 58 | namespace test_make_unsigned_t { 59 | using sg14::make_unsigned_t; 60 | 61 | static_assert( 62 | std::is_same, make_unsigned_t>>::value, 63 | "sg14::make_unsigned_t>"); 64 | static_assert( 65 | std::is_same, make_unsigned_t>>::value, 66 | "sg14::make_unsigned_t>"); 67 | } 68 | 69 | namespace test_to_rep { 70 | using sg14::_impl::to_rep; 71 | 72 | static_assert( 73 | identical(123, to_rep(precise_integer<>{123})), 74 | "sg14::to_rep test failed"); 75 | } 76 | 77 | namespace test_from_rep { 78 | using sg14::_impl::from_rep; 79 | 80 | static_assert( 81 | identical(precise_integer<>{2468}, from_rep>(2468)), 82 | "sg14::from_rep test failed"); 83 | } 84 | 85 | namespace test_from_value { 86 | using sg14::_impl::from_value; 87 | 88 | static_assert(identical(precise_integer{9876543210LL}, from_value>(9876543210LL)), 89 | "sg14::from_value test failed"); 90 | } 91 | } 92 | 93 | namespace test_operate { 94 | using sg14::_impl::equal_tag; 95 | using sg14::_impl::greater_than_tag; 96 | using sg14::_impl::operate; 97 | 98 | static_assert( 99 | operate(precise_integer<>{2468}, precise_integer<>{2468}, equal_tag), 100 | "sg14::numeric_traits test failed"); 101 | static_assert( 102 | operate(2468, precise_integer<>{2468}, equal_tag), 103 | "sg14::numeric_traits test failed"); 104 | static_assert( 105 | operate(precise_integer<>{234}, 233, greater_than_tag), 106 | "sg14::numeric_traits test failed"); 107 | } 108 | 109 | namespace test_comparison { 110 | static_assert( 111 | identical(precise_integer<>{2468}, precise_integer<>{2468}), 112 | "sg14::numeric_traits test failed"); 113 | } 114 | 115 | namespace test_conversion_operator { 116 | static_assert(identical(static_cast(precise_integer<>{9876}), 9876), "precise_integer conversion test failed"); 117 | } 118 | 119 | namespace test_closest_rounding_tag { 120 | using sg14::closest_rounding_tag; 121 | static_assert(identical(closest_rounding_tag::convert(0.), 0), "sg14::closest_rounding_tag test failed"); 122 | static_assert(identical(closest_rounding_tag::convert(-1.), -1), "sg14::closest_rounding_tag test failed"); 123 | } 124 | 125 | namespace closest { 126 | using precise_integer = sg14::precise_integer<>; 127 | 128 | static_assert(precise_integer{0.} == 0, "sg14::precise_integer test failed"); 129 | static_assert(precise_integer{1.} == 1, "sg14::precise_integer test failed"); 130 | static_assert(precise_integer{-1.} == -1, "sg14::precise_integer test failed"); 131 | static_assert(precise_integer{.5} == 1, "sg14::precise_integer test failed"); 132 | static_assert(precise_integer{-.5} == -1, "sg14::precise_integer test failed"); 133 | static_assert(precise_integer{0.499} == 0, "sg14::precise_integer test failed"); 134 | static_assert(precise_integer{-0.501} == -1, "sg14::precise_integer test failed"); 135 | } 136 | 137 | namespace arithmetic { 138 | static_assert(identical(precise_integer<>{3}*precise_integer<>{7}, precise_integer<>{21}), 139 | "precise_integer operator*"); 140 | } 141 | } 142 | 143 | template 144 | struct precise_integer_tests { 145 | // from_rep 146 | static_assert(identical(precise_integer<>{123}, sg14::_impl::from_rep>(123)), 147 | "sg14::_impl::from_rep> test failed"); 148 | 149 | // to_rep 150 | static_assert(identical(123, sg14::_impl::to_rep(123)), "sg14::_impl::to_rep test failed"); 151 | static_assert(identical(321, sg14::_impl::to_rep(precise_integer<>{321})), "sg14::_impl::to_rep test failed"); 152 | }; 153 | 154 | template struct number_test_by_rep_by_tag; 155 | -------------------------------------------------------------------------------- /src/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.4) 2 | if (POLICY CMP0054) 3 | cmake_policy(SET CMP0054 NEW) 4 | endif() 5 | 6 | include("${CMAKE_CURRENT_LIST_DIR}/../common/common.cmake") 7 | 8 | ###################################################################### 9 | # fp_test target 10 | 11 | add_executable(fp_test 12 | # slowest 13 | ${CMAKE_CURRENT_LIST_DIR}/elastic_fixed_point.cpp 14 | 15 | # still pretty slow 16 | ${CMAKE_CURRENT_LIST_DIR}/fixed_point_saturated_integer.cpp 17 | ${CMAKE_CURRENT_LIST_DIR}/fixed_point_native_integer.cpp 18 | ${CMAKE_CURRENT_LIST_DIR}/fixed_point_throwing_integer.cpp 19 | ${CMAKE_CURRENT_LIST_DIR}/safe_elastic_integer.cpp 20 | ${CMAKE_CURRENT_LIST_DIR}/fixed_point_built_in.cpp 21 | 22 | ${CMAKE_CURRENT_LIST_DIR}/overflow.cpp 23 | ${CMAKE_CURRENT_LIST_DIR}/safe_integer.cpp 24 | ${CMAKE_CURRENT_LIST_DIR}/precise_integer.cpp 25 | ${CMAKE_CURRENT_LIST_DIR}/const_integer.cpp 26 | ${CMAKE_CURRENT_LIST_DIR}/utils.cpp 27 | ${CMAKE_CURRENT_LIST_DIR}/boost.multiprecision.cpp 28 | ${CMAKE_CURRENT_LIST_DIR}/boost.simd.cpp 29 | ${CMAKE_CURRENT_LIST_DIR}/common.cpp 30 | ${CMAKE_CURRENT_LIST_DIR}/elastic_integer.cpp 31 | ${CMAKE_CURRENT_LIST_DIR}/glm.cpp 32 | ${CMAKE_CURRENT_LIST_DIR}/index.cpp 33 | ${CMAKE_CURRENT_LIST_DIR}/make_elastic_fixed_point.cpp 34 | ${CMAKE_CURRENT_LIST_DIR}/number_test.cpp 35 | ${CMAKE_CURRENT_LIST_DIR}/numeric.cpp 36 | ${CMAKE_CURRENT_LIST_DIR}/num_traits.cpp 37 | ${CMAKE_CURRENT_LIST_DIR}/p0037.cpp 38 | ${CMAKE_CURRENT_LIST_DIR}/p0381.cpp 39 | ${CMAKE_CURRENT_LIST_DIR}/p0554.cpp 40 | ${CMAKE_CURRENT_LIST_DIR}/p0675.cpp 41 | ${CMAKE_CURRENT_LIST_DIR}/readme.cpp 42 | ${CMAKE_CURRENT_LIST_DIR}/snippets.cpp 43 | ${CMAKE_CURRENT_LIST_DIR}/fixed_point_math.cpp 44 | ${CMAKE_CURRENT_LIST_DIR}/zero_cost_average.cpp 45 | ${CMAKE_CURRENT_LIST_DIR}/zero_cost_free_functions.cpp 46 | ${CMAKE_CURRENT_LIST_DIR}/zero_cost_square.cpp 47 | ${CMAKE_CURRENT_LIST_DIR}/fft.cpp 48 | ${CMAKE_CURRENT_LIST_DIR}/cppnow2017.cpp 49 | 50 | # likely to fail if other files with simpler tests fail 51 | ${CMAKE_CURRENT_LIST_DIR}/precise_elastic_integer.cpp 52 | ${CMAKE_CURRENT_LIST_DIR}/precise_fixed_point.cpp 53 | ${CMAKE_CURRENT_LIST_DIR}/precise_safe_elastic_fixed_point.cpp 54 | ${CMAKE_CURRENT_LIST_DIR}/precise_safe_elastic_integer.cpp 55 | 56 | # pointless unless the rest compiles 57 | ${CMAKE_CURRENT_LIST_DIR}/main.cpp 58 | ) 59 | 60 | ###################################################################### 61 | # find external lib, boost.multiprecision 62 | 63 | find_package(Boost) 64 | 65 | if(Boost_FOUND) 66 | if(Boost_VERSION GREATER 105500 OR EXCEPTIONS) 67 | # only compile multiprecision.cpp if Boost.Multiprecision is available 68 | set(BOOST_FOUND_CXX_FLAGS "-DSG14_BOOST_ENABLED") 69 | target_include_directories(fp_test SYSTEM PUBLIC ${Boost_INCLUDE_DIR}) 70 | target_link_libraries(fp_test ${Boost_LIBRARIES}) 71 | else(Boost_VERSION GREATER 105500 OR EXCEPTIONS) 72 | message("Boost version is too low to use without exception handling") 73 | endif(Boost_VERSION GREATER 105500 OR EXCEPTIONS) 74 | else(Boost_FOUND) 75 | message("Boost not found") 76 | endif(Boost_FOUND) 77 | 78 | ###################################################################### 79 | # pull external project, boost.simd 80 | 81 | if(Boost_FOUND AND Boost_VERSION GREATER 106099) 82 | ExternalProject_Add( 83 | boost.simd 84 | URL "https://github.com/johnmcfarlane/boost.simd/archive/5d67a72d77411dc610126151a182ebbae1bfbac6.zip" 85 | URL_MD5 "97d1aad967965ee79a751d099cd0ee6f" 86 | UPDATE_COMMAND "" 87 | INSTALL_COMMAND "" 88 | ) 89 | 90 | ExternalProject_Get_Property(boost.simd source_dir) 91 | target_include_directories( 92 | fp_test 93 | PRIVATE 94 | SYSTEM 95 | "${source_dir}/include" 96 | ) 97 | 98 | add_dependencies( 99 | fp_test 100 | boost.simd 101 | ) 102 | 103 | set(BOOST_SIMD_FOUND_CXX_FLAGS "-DSG14_BOOST_SIMD_ENABLED") 104 | endif(Boost_FOUND AND Boost_VERSION GREATER 106099) 105 | 106 | ###################################################################### 107 | # pull external project, glm 108 | 109 | ExternalProject_Add( 110 | glm 111 | URL "https://github.com/johnmcfarlane/glm/archive/878086621522255b14017c26d1df3b9fb0b36009.zip" 112 | URL_MD5 "fe3b032c230df63474bc26549526aaec" 113 | UPDATE_COMMAND "" 114 | INSTALL_COMMAND "" 115 | ) 116 | 117 | ExternalProject_Get_Property(glm source_dir) 118 | target_include_directories( 119 | fp_test 120 | PRIVATE 121 | SYSTEM 122 | ${source_dir} 123 | ) 124 | 125 | add_dependencies( 126 | fp_test 127 | glm 128 | ) 129 | 130 | ###################################################################### 131 | # pull external project, google_test 132 | 133 | ExternalProject_Add( 134 | google_test 135 | URL "https://github.com/google/googletest/archive/c99458533a9b4c743ed51537e25989ea55944908.zip" 136 | URL_MD5 "4552721bde3dcaab1eaa9582afc28c9d" 137 | UPDATE_COMMAND "" 138 | INSTALL_COMMAND "" 139 | CMAKE_ARGS -Dgtest_force_shared_crt=ON 140 | ) 141 | 142 | ExternalProject_Get_Property(google_test source_dir) 143 | target_include_directories( 144 | fp_test 145 | PRIVATE 146 | SYSTEM 147 | ${source_dir}/include 148 | ) 149 | 150 | ExternalProject_Get_Property(google_test binary_dir) 151 | if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) 152 | target_link_libraries( 153 | fp_test 154 | debug ${binary_dir}/Debug/${CMAKE_FIND_LIBRARY_PREFIXES}gtest${CMAKE_FIND_LIBRARY_SUFFIXES} 155 | optimized ${binary_dir}/Release/${CMAKE_FIND_LIBRARY_PREFIXES}gtest${CMAKE_FIND_LIBRARY_SUFFIXES} 156 | ) 157 | else () 158 | target_link_libraries( 159 | fp_test 160 | general ${binary_dir}/${CMAKE_FIND_LIBRARY_PREFIXES}gtest.a 161 | general pthread 162 | ) 163 | endif () 164 | 165 | add_dependencies( 166 | fp_test 167 | google_test 168 | ) 169 | 170 | ###################################################################### 171 | # fp_test compiler flags 172 | 173 | set_target_properties( 174 | fp_test 175 | PROPERTIES COMPILE_FLAGS "${COMMON_CXX_FLAGS} ${BOOST_FOUND_CXX_FLAGS} ${BOOST_SIMD_FOUND_CXX_FLAGS}" 176 | ) 177 | 178 | # avoid GCC6 warning 179 | # http://stackoverflow.com/a/38650955/671509 180 | if (${CMAKE_COMPILER_IS_GNUCXX}) 181 | set_source_files_properties( 182 | ${CMAKE_CURRENT_LIST_DIR}/boost.multiprecision.cpp 183 | ${CMAKE_CURRENT_LIST_DIR}/index.cpp 184 | PROPERTIES COMPILE_FLAGS "-fpermissive") 185 | endif () 186 | 187 | ###################################################################### 188 | # testing 189 | 190 | enable_testing() 191 | add_test(all_tests fp_test) 192 | -------------------------------------------------------------------------------- /include/sg14/bits/common.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | // definitions that are directly required by more than one header of the API 8 | 9 | #if !defined(SG14_COMMON_H) 10 | #define SG14_COMMON_H 1 11 | 12 | #include 13 | 14 | namespace sg14 { 15 | namespace _impl { 16 | //////////////////////////////////////////////////////////////////////////////// 17 | // sg14::_impl::max 18 | 19 | template 20 | constexpr T max(T a, T b) 21 | { 22 | return (a 29 | constexpr T min(T a, T b) 30 | { 31 | return (a 51 | constexpr auto operator()(const Rhs& rhs) const -> decltype(-rhs) 52 | { 53 | return -rhs; 54 | } 55 | }; 56 | 57 | struct plus_op : arithmetic_op { 58 | template 59 | constexpr auto operator()(const Rhs& rhs) const -> decltype(+rhs) 60 | { 61 | return +rhs; 62 | } 63 | }; 64 | 65 | struct add_op : arithmetic_op { 66 | template 67 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs+rhs) 68 | { 69 | return lhs+rhs; 70 | } 71 | }; 72 | 73 | struct subtract_op : arithmetic_op { 74 | template 75 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs-rhs) 76 | { 77 | return lhs-rhs; 78 | } 79 | }; 80 | 81 | struct multiply_op : arithmetic_op { 82 | template 83 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs*rhs) 84 | { 85 | return lhs*rhs; 86 | } 87 | }; 88 | 89 | struct divide_op : arithmetic_op { 90 | template 91 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs/rhs) 92 | { 93 | return lhs/rhs; 94 | } 95 | }; 96 | 97 | struct bitwise_or_op : arithmetic_op { 98 | template 99 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs|rhs) 100 | { 101 | return lhs|rhs; 102 | } 103 | }; 104 | 105 | struct bitwise_and_op : arithmetic_op { 106 | template 107 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs&rhs) 108 | { 109 | return lhs&rhs; 110 | } 111 | }; 112 | 113 | struct bitwise_xor_op : arithmetic_op { 114 | template 115 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs^rhs) 116 | { 117 | return lhs^rhs; 118 | } 119 | }; 120 | 121 | struct equal_op : comparison_op { 122 | template 123 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs==rhs) 124 | { 125 | return lhs==rhs; 126 | } 127 | }; 128 | 129 | struct not_equal_op : comparison_op { 130 | template 131 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs!=rhs) 132 | { 133 | return lhs!=rhs; 134 | } 135 | }; 136 | 137 | struct less_than_op : comparison_op { 138 | template 139 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs 147 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs>rhs) 148 | { 149 | return lhs>rhs; 150 | } 151 | }; 152 | 153 | struct less_than_or_equal_op : comparison_op { 154 | template 155 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs<=rhs) 156 | { 157 | return lhs<=rhs; 158 | } 159 | }; 160 | 161 | struct greater_than_or_equal_op : comparison_op { 162 | template 163 | constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs>=rhs) 164 | { 165 | return lhs>=rhs; 166 | } 167 | }; 168 | 169 | static constexpr plus_op plus_tag {}; 170 | static constexpr minus_op minus_tag {}; 171 | 172 | static constexpr add_op add_tag {}; 173 | static constexpr subtract_op subtract_tag {}; 174 | static constexpr multiply_op multiply_tag {}; 175 | static constexpr divide_op divide_tag {}; 176 | 177 | static constexpr bitwise_or_op bitwise_or_tag {}; 178 | static constexpr bitwise_and_op bitwise_and_tag {}; 179 | static constexpr bitwise_xor_op bitwise_xor_tag {}; 180 | 181 | static constexpr equal_op equal_tag {}; 182 | static constexpr not_equal_op not_equal_tag {}; 183 | static constexpr less_than_op less_than_tag {}; 184 | static constexpr greater_than_op greater_than_tag {}; 185 | static constexpr less_than_or_equal_op less_than_or_equal_tag {}; 186 | static constexpr greater_than_or_equal_op greater_than_or_equal_tag {}; 187 | 188 | //////////////////////////////////////////////////////////////////////////////// 189 | // sg14::_impl::rep_op_result 190 | 191 | template 192 | using op_result = decltype(Operator()(std::declval(), std::declval())); 193 | } 194 | } 195 | 196 | #endif // SG14_COMMON_H 197 | -------------------------------------------------------------------------------- /src/test/index.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | // example code presented on the front page of the Doxygen documentation 8 | 9 | //////////////////////////////////////////////////////////////////////////////// 10 | // test_function - run given function and test its output 11 | 12 | #include 13 | #include 14 | 15 | // calls the given function and checks that it produces the expected output 16 | void test_function(void(* function)(), char const* output) 17 | { 18 | // substitute cout for a string 19 | std::stringstream captured_cout; 20 | std::streambuf* coutbuf = std::cout.rdbuf(); 21 | std::cout.rdbuf(captured_cout.rdbuf()); //redirect cout to out.txt! 22 | 23 | // run example from documentation 24 | function(); 25 | 26 | // restore cout 27 | std::cout.rdbuf(coutbuf); //reset to standard output again 28 | 29 | // test the content of the string 30 | ASSERT_EQ(output, captured_cout.str()); 31 | } 32 | 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | //! [declaration example] 36 | #include 37 | #include 38 | 39 | using namespace sg14; 40 | using namespace std; 41 | 42 | void declaration_example() 43 | { 44 | // x is represented by an int and scaled down by 1 bit 45 | auto x = fixed_point{3.5}; 46 | 47 | // another way to specify a fixed-point type is with make_fixed or make_ufixed 48 | auto y = make_fixed<30, 1>{3.5}; // (s30:1) 49 | static_assert(is_same::value, ""); // assumes that int is 32-bit 50 | 51 | // under the hood, x stores a whole number 52 | cout << x.data() << endl; // "7" 53 | 54 | // but it multiplies that whole number by 2^-1 to produce a real number 55 | cout << x << endl; // "3.5" 56 | 57 | // like an int, x has limited precision 58 | x /= 2; 59 | cout << x << endl; // "1.5" 60 | } 61 | //! [declaration example] 62 | 63 | TEST(index, declaration_example) 64 | { 65 | test_function(declaration_example, "7\n3.5\n1.5\n"); 66 | } 67 | 68 | 69 | //////////////////////////////////////////////////////////////////////////////// 70 | //! [basic arithmetic example] 71 | void basic_arithmetic_example() 72 | { 73 | // define a constant signed value with 3 integer and 28 fractional bits (s3:28) 74 | auto pi = fixed_point{3.1415926535}; 75 | 76 | // expressions involving integers return fixed_point results 77 | auto tau = pi*2; 78 | static_assert(is_same>::value, ""); 79 | 80 | // "6.28319" 81 | cout << tau << endl; 82 | 83 | // expressions involving floating-point values return floating-point results 84 | auto degrees = tau*(180/3.1415926534); 85 | static_assert(is_same::value, ""); 86 | 87 | // "360" 88 | cout << degrees << '\n'; 89 | } 90 | //! [basic arithmetic example] 91 | 92 | TEST(index, basic_arithmetic_example) 93 | { 94 | test_function(basic_arithmetic_example, "6.28319\n360\n"); 95 | } 96 | 97 | 98 | //////////////////////////////////////////////////////////////////////////////// 99 | //! [advanced arithmetic example] 100 | #include 101 | 102 | void advanced_arithmetic_example() 103 | { 104 | // this variable uses all of its capacity 105 | auto x = fixed_point{15.9375}; 106 | 107 | // 15.9375 * 15.9375 = 254.00390625 ... overflow! 108 | cout << fixed_point{x*x} << endl; // "14" instead! 109 | 110 | // fixed-point multiplication operator widens result 111 | auto xx = x*x; 112 | 113 | // x*x is promoted to fixed_point 114 | static_assert(is_same>::value, ""); 115 | cout << setprecision(12) << xx << endl; // "254.00390625" - correct 116 | 117 | // you can avoid the pitfalls of integer promotion using the multiply function 118 | auto named_xx = multiply(x, x); 119 | 120 | // multiply result is same as underlying representation's operation 121 | static_assert(is_same>::value, ""); 122 | cout << named_xx << endl; // "254.00390625" - also correct but prone to overflow 123 | } 124 | //! [advanced arithmetic example] 125 | 126 | TEST(index, advanced_arithmetic_example) 127 | { 128 | test_function(advanced_arithmetic_example, "14\n254.00390625\n254.00390625\n"); 129 | } 130 | 131 | 132 | #if defined(SG14_BOOST_ENABLED) 133 | //////////////////////////////////////////////////////////////////////////////// 134 | //! [boost example] 135 | #include 136 | 137 | void boost_example() 138 | { 139 | using namespace boost::multiprecision; 140 | using rep = number>; 141 | 142 | // Define an unsigned type with 400 integer digits and 0 fractional digits. 143 | using big_number = fixed_point; 144 | 145 | // a googol is 10^100 146 | auto googol = big_number{1}; 147 | for (auto zeros = 0; zeros!=100; ++zeros) { 148 | googol *= 10; 149 | } 150 | 151 | // "1e+100" 152 | cout << googol << endl; 153 | 154 | // Dividing a s31:0 number by a u400:0 number 155 | auto googolth = 1 / googol; 156 | 157 | // produces a s31::400 number. 158 | static_assert(is_same>::value, ""); 159 | 160 | // Prints "1e-100" (although this value is only approximate). 161 | cout << googolth << endl; 162 | } 163 | //! [boost example] 164 | 165 | TEST(index, boost_example) 166 | { 167 | test_function(boost_example, "1e+100\n1e-100\n"); 168 | } 169 | #endif 170 | 171 | 172 | //////////////////////////////////////////////////////////////////////////////// 173 | //! [elastic example] 174 | #include 175 | 176 | void elastic_example1() 177 | { 178 | // Consider an integer type which keeps count of the bits that it uses. 179 | auto a = elastic_integer<6, int8_t>{ 63 }; 180 | 181 | // Results of its operations widen as required. 182 | auto aa = a*a; 183 | static_assert(is_same> ::value, ""); 184 | 185 | // Obviously, this type no longer fits in a byte. 186 | static_assert(sizeof(aa)==2, ""); 187 | 188 | // Addition requires smaller results. 189 | auto a2 = a+a; 190 | static_assert(is_same> ::value, ""); 191 | } 192 | 193 | // Such a type can be used to specialize fixed_point. 194 | template 195 | using elastic_fixed_point = fixed_point, -FractionalDigits>; 196 | 197 | void elastic_example2() 198 | { 199 | // Now arithmetic operations are more efficient and less error-prone. 200 | auto b = elastic_fixed_point<4, 28, unsigned>{15.9375}; 201 | auto bb = b*b; 202 | 203 | cout << bb << endl; // "254.00390625" 204 | static_assert(is_same>::value, ""); 205 | } 206 | //! [elastic example] 207 | 208 | TEST(index, elastic_example) 209 | { 210 | test_function(elastic_example1, ""); 211 | test_function(elastic_example2, "254.00390625\n"); 212 | } 213 | -------------------------------------------------------------------------------- /src/test/cppnow2017.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #if (__cplusplus>=201402L) 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | using namespace sg14; 20 | using namespace std; 21 | using _impl::identical; 22 | 23 | namespace stl { 24 | template 25 | using Composite = map>>; 26 | } 27 | 28 | namespace prototypes { 29 | template 30 | class safe_integer { 31 | public: 32 | template 33 | constexpr safe_integer(Input const& r) 34 | :_rep(r) { } 35 | 36 | constexpr Rep const& data() const { return _rep; } 37 | 38 | private: 39 | Rep _rep; 40 | }; 41 | 42 | template 43 | class elastic_integer { 44 | // ... 45 | private: 46 | using Rep = Narrowest; 47 | Rep _rep; // Narrowest or something wider 48 | }; 49 | 50 | // good 51 | template 52 | class good_safe_integer; 53 | 54 | using good1 = good_safe_integer; 55 | 56 | using good2 = safe_integer; 57 | 58 | // bad 59 | template 60 | class bad_safe_integer; 61 | 62 | using bad1 = bad_safe_integer<31, true>; 63 | 64 | using bad2 = bad_safe_integer::digits, true>; 65 | } 66 | 67 | #if defined(SG14_EXCEPTIONS_ENABLED) 68 | TEST(cppnow2017, safe_integer_example) 69 | { 70 | // multiplication of safe_integer cannot exceed numeric limits 71 | EXPECT_THROW(safe_integer{numeric_limits::max()}*2, overflow_error); 72 | 73 | // difference from safe_integer cannot be negative 74 | EXPECT_THROW(safe_integer{0}-1, overflow_error); 75 | 76 | // conversion to safe_integer cannot exceed numeric limits 77 | EXPECT_THROW(safe_integer{numeric_limits::max()}, overflow_error); 78 | 79 | // value of safe_integer cannot be indeterminate 80 | //auto d = safe_integer{}; // compiler error? exception? zero-initialization? 81 | } 82 | #endif 83 | 84 | namespace elastic_integer_example { 85 | // elastic_integer holding 4 digits 86 | constexpr auto a = elastic_integer<4, unsigned>{10}; 87 | static_assert(identical(a, elastic_integer<4, unsigned>{10}), "error in CppNow 2017 slide"); 88 | 89 | // result of addition is 1 digit wider 90 | constexpr auto b = a+a; // elastic_integer<5, unsigned>; 91 | static_assert(identical(b, elastic_integer<5, unsigned>{20}), "error in CppNow 2017 slide"); 92 | 93 | // result of subtraction is signed 94 | constexpr auto c = -b; // elastic_integer<5, signed>; 95 | static_assert(identical(c, elastic_integer<5, signed>{-20}), "error in CppNow 2017 slide"); 96 | 97 | // run-time overflow is not my concern 98 | constexpr auto d = elastic_integer<8, signed>{256}; 99 | static_assert(identical(d, elastic_integer<8, signed>{256}), "error in CppNow 2017 slide"); 100 | static_assert(d>numeric_limits::max(), "error in CppNow 2017 slide"); 101 | } 102 | 103 | namespace acme_ndebug { 104 | #define _NDEBUG 105 | 106 | namespace acme { 107 | #if defined(_NDEBUG) 108 | template 109 | using integer = Rep; 110 | #else 111 | template 112 | using integer = safe_integer; 113 | #endif 114 | } 115 | 116 | static_assert(is_same, int>::value, "error in CppNow 2017 slide"); 117 | 118 | auto square(acme::integer f) 119 | { 120 | return f*f; 121 | } 122 | 123 | static_assert(is_same::value, "error in CppNow 2017 slide"); 124 | } 125 | 126 | namespace acme_debug { 127 | namespace acme { 128 | #if defined(NDEBUG) 129 | template 130 | using integer = Rep; 131 | #else 132 | template 133 | using integer = safe_integer; 134 | #endif 135 | } 136 | 137 | #if defined(NDEBUG) 138 | static_assert(is_same, int>::value, "error in CppNow 2017 slide"); 139 | #else 140 | static_assert(is_same, safe_integer>::value, "error in CppNow 2017 slide"); 141 | #endif 142 | 143 | auto square(acme::integer f) 144 | { 145 | return f*f; 146 | } 147 | 148 | #if defined(NDEBUG) 149 | static_assert(is_same::value, "error in CppNow 2017 slide"); 150 | #else 151 | static_assert(is_same>::value, "error in CppNow 2017 slide"); 152 | #endif 153 | } 154 | 155 | namespace operator_overload1 { 156 | using prototypes::safe_integer; 157 | 158 | template 159 | auto operator*(safe_integer const& a, safe_integer const& b) 160 | { 161 | Rep product = a.data()*b.data(); 162 | 163 | // do some overflow checking 164 | 165 | return safe_integer{product}; 166 | } 167 | 168 | static_assert( 169 | is_same{2}*safe_integer{3}), safe_integer>::value, 170 | "error in CppNow 2017 slide"); 171 | 172 | // error: no match for ‘operator*’ 173 | //auto x = safe_integer{6} * safe_integer{7}; 174 | } 175 | 176 | namespace operator_overload2 { 177 | using prototypes::safe_integer; 178 | 179 | template 180 | auto operator*(safe_integer const& a, safe_integer const& b) 181 | { 182 | auto product = a.data()*b.data(); 183 | 184 | // do some overflow checking 185 | 186 | return safe_integer{product}; 187 | } 188 | 189 | static_assert( 190 | is_same{2}*safe_integer{3}), safe_integer>::value, 191 | "error in CppNow 2017 slide"); 192 | 193 | static_assert( 194 | is_same{6}*safe_integer{7}), safe_integer>::value, 195 | "error in CppNow 2017 slide"); 196 | } 197 | 198 | namespace composite { 199 | using prototypes::safe_integer; 200 | 201 | template 202 | using elastic_integer = sg14::elastic_integer; 203 | 204 | template 205 | using safe_elastic_integer = 206 | safe_integer>; 207 | 208 | template 209 | constexpr auto operator*(safe_integer const& a, safe_integer const& b) 210 | { 211 | auto product = a.data()*b.data(); 212 | 213 | if (numeric_limits::digits+numeric_limits::digits 214 | >numeric_limits::digits) { 215 | // do some overflow checking 216 | } 217 | 218 | return safe_integer{product}; 219 | } 220 | 221 | auto a = safe_elastic_integer<4>{14}*safe_elastic_integer<3>{6}; 222 | static_assert(is_same>::value, "error in CppNow 2017 slide"); 223 | } 224 | 225 | #endif // (__cplusplus>=201402L) 226 | -------------------------------------------------------------------------------- /src/test/multiprecision.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #if defined(SG14_BOOST_ENABLED) 8 | 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | 15 | using sg14::fixed_point; 16 | using sg14::multiprecision; 17 | using sg14::set_digits_t; 18 | using sg14::signed_multiprecision; 19 | using sg14::unsigned_multiprecision; 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | // sg14::multiprecision traits 23 | 24 | // test sg14::is_signed<{un}signed_multiprecision>::value 25 | static_assert(sg14::is_signed>::value, 26 | "sg14::is_signed>::value test failed"); 27 | static_assert(sg14::is_signed>::value, 28 | "sg14::is_signed>::value test failed"); 29 | static_assert(!sg14::is_signed>::value, 30 | "sg14::is_signed>::value test failed"); 31 | static_assert(!sg14::is_signed>::value, 32 | "sg14::is_signed>::value test failed"); 33 | 34 | // test sg14::make_signed<{un}signed_multiprecision>::make_signed 35 | static_assert(sg14::is_signed>>::value, 36 | "sg14::make_signed<{un}signed_multiprecision>::make_signed test failed"); 37 | static_assert(sg14::is_signed>>::value, 38 | "sg14::make_signed<{un}signed_multiprecision>::make_signed test failed"); 39 | static_assert(sg14::is_signed>>::value, 40 | "sg14::make_signed<{un}signed_multiprecision>::make_signed test failed"); 41 | static_assert(sg14::is_signed>>::value, 42 | "sg14::make_signed<{un}signed_multiprecision>::make_signed test failed"); 43 | 44 | // test sg14::make_unsigned_t<{un}signed_multiprecision> 45 | static_assert(!sg14::is_signed>>::value, 46 | "sg14::make_unsigned<{un}signed_multiprecision>::make_unsigned test failed"); 47 | static_assert(!sg14::is_signed>>::value, 48 | "sg14::make_unsigned<{un}signed_multiprecision>::make_unsigned test failed"); 49 | static_assert(!sg14::is_signed>>::value, 50 | "sg14::make_unsigned<{un}signed_multiprecision>::make_unsigned test failed"); 51 | static_assert(!sg14::is_signed>>::value, 52 | "sg14::make_unsigned<{un}signed_multiprecision>::make_unsigned test failed"); 53 | 54 | // test sg14::width<{un}signed_multiprecision> 55 | static_assert(sg14::digits>::value>=11, "sg14::digits::value test failed"); 56 | static_assert(sg14::digits>::value>=12, "sg14::digits::value test failed"); 57 | 58 | // test sg14::set_digits_t<{un}signed_multiprecision> 59 | static_assert(sg14::digits, 57>>::value>=57, "set_digits_t test failed"); 60 | static_assert(sg14::digits, 3>>::value>=3, "set_digits_t test failed"); 61 | 62 | static_assert(sg14::digits, 3>>::value<=sg14::digits>::value, "set_digits_t test failed"); 63 | static_assert(sg14::digits>::value>=sg14::digits, 16>>::value, "set_digits_t test failed"); 64 | 65 | //////////////////////////////////////////////////////////////////////////////// 66 | // sg14::multiprecision arithmetic 67 | 68 | TEST(multiprecision, add) 69 | { 70 | using int64 = unsigned_multiprecision<64>; 71 | 72 | auto augend = int64{123456789012345678LL}; 73 | auto addend = int64{876543210987654321LL}; 74 | 75 | auto sum = augend+addend; 76 | auto expected = int64{999999999999999999LL}; 77 | 78 | ASSERT_EQ(sum, expected); 79 | } 80 | 81 | TEST(multiprecision, subtract) 82 | { 83 | using int64 = sg14::unsigned_multiprecision<64>; 84 | 85 | auto minuend = int64{999999999999999999LL}; 86 | auto subtrahend = int64{876543210987654321LL}; 87 | 88 | auto difference = minuend-subtrahend; 89 | auto expected = int64{123456789012345678LL}; 90 | 91 | ASSERT_EQ(difference, expected); 92 | } 93 | 94 | TEST(multiprecision, multiply) 95 | { 96 | using int64 = unsigned_multiprecision<64>; 97 | using int128 = unsigned_multiprecision<128>; 98 | 99 | auto factor = int64{123456789012345678LL}; 100 | 101 | auto product = static_cast(factor)*static_cast(factor); 102 | ASSERT_GT(product, factor); 103 | 104 | auto quotient = product/factor; 105 | ASSERT_EQ(factor, quotient); 106 | } 107 | 108 | TEST(multiprecision, divide) 109 | { 110 | using int64 = unsigned_multiprecision<64>; 111 | 112 | auto div = int64{123456789012345678LL}; 113 | 114 | auto quotient = div/div; 115 | auto expected = 1; 116 | 117 | ASSERT_EQ(quotient, expected); 118 | } 119 | 120 | //////////////////////////////////////////////////////////////////////////////// 121 | // sg14::fixed_point> arithmetic 122 | 123 | TEST(fixed_point_multiprecision, add) 124 | { 125 | using int64 = fixed_point>; 126 | 127 | auto augend = int64{123456789012345678LL}; 128 | auto addend = int64{876543210987654321LL}; 129 | 130 | auto sum = augend+addend; 131 | auto expected = int64{999999999999999999LL}; 132 | 133 | ASSERT_EQ(sum, expected); 134 | } 135 | 136 | TEST(fixed_point_multiprecision, subtract) 137 | { 138 | using int64 = fixed_point>; 139 | 140 | auto minuend = int64{999999999999999999LL}; 141 | auto subtrahend = int64{876543210987654321LL}; 142 | 143 | auto difference = minuend-subtrahend; 144 | auto expected = int64{123456789012345678LL}; 145 | 146 | ASSERT_EQ(difference, expected); 147 | } 148 | 149 | TEST(fixed_point_multiprecision, multiply) 150 | { 151 | using int64 = fixed_point>; 152 | using int128 = fixed_point>; 153 | 154 | auto factor = int64{123456789012345678LL}; 155 | 156 | auto product = static_cast(factor)*static_cast(factor); 157 | ASSERT_GT(product, factor); 158 | 159 | auto quotient = product/factor; 160 | ASSERT_EQ(factor, quotient); 161 | } 162 | 163 | TEST(fixed_point_multiprecision, divide) 164 | { 165 | using int64 = fixed_point>; 166 | 167 | auto div = int64{123456789012345678LL}; 168 | 169 | auto quotient = div/div; 170 | auto expected = 1; 171 | 172 | ASSERT_EQ(quotient, expected); 173 | } 174 | 175 | //////////////////////////////////////////////////////////////////////////////// 176 | // boost::throw_exception 177 | 178 | #if defined(BOOST_NO_EXCEPTIONS) 179 | namespace boost { 180 | void throw_exception(std::exception const &) { 181 | std::terminate(); 182 | } 183 | } 184 | #endif 185 | 186 | #endif // SG14_BOOST_ENABLED 187 | -------------------------------------------------------------------------------- /src/test/boost.multiprecision.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright John McFarlane 2015 - 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #if defined(SG14_BOOST_ENABLED) 8 | 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | 15 | using sg14::fixed_point; 16 | using sg14::multiprecision; 17 | using sg14::set_digits_t; 18 | using sg14::signed_multiprecision; 19 | using sg14::unsigned_multiprecision; 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | // sg14::multiprecision traits 23 | 24 | // test sg14::is_signed<{un}signed_multiprecision>::value 25 | static_assert(sg14::is_signed>::value, 26 | "sg14::is_signed>::value test failed"); 27 | static_assert(sg14::is_signed>::value, 28 | "sg14::is_signed>::value test failed"); 29 | static_assert(!sg14::is_signed>::value, 30 | "sg14::is_signed>::value test failed"); 31 | static_assert(!sg14::is_signed>::value, 32 | "sg14::is_signed>::value test failed"); 33 | 34 | // test sg14::make_signed<{un}signed_multiprecision>::make_signed 35 | static_assert(sg14::is_signed>>::value, 36 | "sg14::make_signed<{un}signed_multiprecision>::make_signed test failed"); 37 | static_assert(sg14::is_signed>>::value, 38 | "sg14::make_signed<{un}signed_multiprecision>::make_signed test failed"); 39 | static_assert(sg14::is_signed>>::value, 40 | "sg14::make_signed<{un}signed_multiprecision>::make_signed test failed"); 41 | static_assert(sg14::is_signed>>::value, 42 | "sg14::make_signed<{un}signed_multiprecision>::make_signed test failed"); 43 | 44 | // test sg14::make_unsigned_t<{un}signed_multiprecision> 45 | static_assert(!sg14::is_signed>>::value, 46 | "sg14::make_unsigned<{un}signed_multiprecision>::make_unsigned test failed"); 47 | static_assert(!sg14::is_signed>>::value, 48 | "sg14::make_unsigned<{un}signed_multiprecision>::make_unsigned test failed"); 49 | static_assert(!sg14::is_signed>>::value, 50 | "sg14::make_unsigned<{un}signed_multiprecision>::make_unsigned test failed"); 51 | static_assert(!sg14::is_signed>>::value, 52 | "sg14::make_unsigned<{un}signed_multiprecision>::make_unsigned test failed"); 53 | 54 | // test sg14::width<{un}signed_multiprecision> 55 | static_assert(sg14::digits>::value>=11, "sg14::digits::value test failed"); 56 | static_assert(sg14::digits>::value>=12, "sg14::digits::value test failed"); 57 | 58 | // test sg14::set_digits_t<{un}signed_multiprecision> 59 | static_assert(sg14::digits, 57>>::value>=57, "set_digits_t test failed"); 60 | static_assert(sg14::digits, 3>>::value>=3, "set_digits_t test failed"); 61 | 62 | static_assert(sg14::digits, 3>>::value<=sg14::digits>::value, "set_digits_t test failed"); 63 | static_assert(sg14::digits>::value>=sg14::digits, 16>>::value, "set_digits_t test failed"); 64 | 65 | //////////////////////////////////////////////////////////////////////////////// 66 | // sg14::multiprecision arithmetic 67 | 68 | TEST(multiprecision, add) 69 | { 70 | using int64 = unsigned_multiprecision<64>; 71 | 72 | auto augend = int64{123456789012345678LL}; 73 | auto addend = int64{876543210987654321LL}; 74 | 75 | auto sum = augend+addend; 76 | auto expected = int64{999999999999999999LL}; 77 | 78 | ASSERT_EQ(sum, expected); 79 | } 80 | 81 | TEST(multiprecision, subtract) 82 | { 83 | using int64 = sg14::unsigned_multiprecision<64>; 84 | 85 | auto minuend = int64{999999999999999999LL}; 86 | auto subtrahend = int64{876543210987654321LL}; 87 | 88 | auto difference = minuend-subtrahend; 89 | auto expected = int64{123456789012345678LL}; 90 | 91 | ASSERT_EQ(difference, expected); 92 | } 93 | 94 | TEST(multiprecision, multiply) 95 | { 96 | using int64 = unsigned_multiprecision<64>; 97 | using int128 = unsigned_multiprecision<128>; 98 | 99 | auto factor = int64{123456789012345678LL}; 100 | 101 | auto product = static_cast(factor)*static_cast(factor); 102 | ASSERT_GT(product, factor); 103 | 104 | auto quotient = product/factor; 105 | ASSERT_EQ(factor, quotient); 106 | } 107 | 108 | TEST(multiprecision, divide) 109 | { 110 | using int64 = unsigned_multiprecision<64>; 111 | 112 | auto div = int64{123456789012345678LL}; 113 | 114 | auto quotient = div/div; 115 | auto expected = 1; 116 | 117 | ASSERT_EQ(quotient, expected); 118 | } 119 | 120 | //////////////////////////////////////////////////////////////////////////////// 121 | // sg14::fixed_point> arithmetic 122 | 123 | TEST(fixed_point_multiprecision, add) 124 | { 125 | using int64 = fixed_point>; 126 | 127 | auto augend = int64{123456789012345678LL}; 128 | auto addend = int64{876543210987654321LL}; 129 | 130 | auto sum = augend+addend; 131 | auto expected = int64{999999999999999999LL}; 132 | 133 | ASSERT_EQ(sum, expected); 134 | } 135 | 136 | TEST(fixed_point_multiprecision, subtract) 137 | { 138 | using int64 = fixed_point>; 139 | 140 | auto minuend = int64{999999999999999999LL}; 141 | auto subtrahend = int64{876543210987654321LL}; 142 | 143 | auto difference = minuend-subtrahend; 144 | auto expected = int64{123456789012345678LL}; 145 | 146 | ASSERT_EQ(difference, expected); 147 | } 148 | 149 | TEST(fixed_point_multiprecision, multiply) 150 | { 151 | using int64 = fixed_point>; 152 | using int128 = fixed_point>; 153 | 154 | auto factor = int64{123456789012345678LL}; 155 | 156 | auto product = static_cast(factor)*static_cast(factor); 157 | ASSERT_GT(product, factor); 158 | 159 | auto quotient = product/factor; 160 | ASSERT_EQ(factor, quotient); 161 | } 162 | 163 | TEST(fixed_point_multiprecision, divide) 164 | { 165 | using int64 = fixed_point>; 166 | 167 | auto div = int64{123456789012345678LL}; 168 | 169 | auto quotient = div/div; 170 | auto expected = 1; 171 | 172 | ASSERT_EQ(quotient, expected); 173 | } 174 | 175 | //////////////////////////////////////////////////////////////////////////////// 176 | // boost::throw_exception 177 | 178 | #if defined(BOOST_NO_EXCEPTIONS) 179 | namespace boost { 180 | void throw_exception(std::exception const &) { 181 | std::terminate(); 182 | } 183 | } 184 | #endif 185 | 186 | #endif // SG14_BOOST_ENABLED 187 | -------------------------------------------------------------------------------- /include/sg14/bits/fixed_point_math.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright Timo Alho 2016. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file ../LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | /// \file 8 | /// \brief some cmath specializations for `sg14::fixed_point` type; 9 | 10 | #ifndef FIXED_POINT_MATH_H_ 11 | #define FIXED_POINT_MATH_H_ 12 | 13 | #include 14 | 15 | /// study group 14 of the C++ working group 16 | namespace sg14 { 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | // implementation-specific definitions 20 | 21 | namespace _impl { 22 | namespace fp { 23 | 24 | template 25 | constexpr FixedPoint rounding_conversion(double d) { 26 | using one_longer = make_fixed; 27 | return FixedPoint::from_data(static_cast((one_longer{ d }.data() + 1) >> 1)); 28 | } 29 | 30 | template 31 | using unsigned_rep = typename std::make_unsigned::type; 32 | 33 | template 34 | using make_largest_ufraction = fixed_point, -std::numeric_limits>::digits>; 35 | 36 | static_assert(std::is_same>, fixed_point>::value, ""); 37 | 38 | //TODO: template magic to get the coefficients automatically 39 | //from the number of bits of precision 40 | //Define the coefficients as constexpr, 41 | //to make sure they're converted to fp 42 | //at compile time 43 | template 44 | struct poly_coeffs { 45 | static constexpr CoeffType a1 { rounding_conversion(0.6931471860838825) }; 46 | static constexpr CoeffType a2 { rounding_conversion(0.2402263846181129) }; 47 | static constexpr CoeffType a3 { rounding_conversion( 48 | 0.055505126858894846) }; 49 | static constexpr CoeffType a4 { rounding_conversion( 50 | 0.009614017013719252) }; 51 | static constexpr CoeffType a5 { rounding_conversion( 52 | 0.0013422634797558564) }; 53 | static constexpr CoeffType a6 { rounding_conversion( 54 | 0.00014352314226313836) }; 55 | static constexpr CoeffType a7 { rounding_conversion( 56 | 0.000021498763160402416) }; 57 | }; 58 | 59 | template 60 | constexpr CoeffType poly_coeffs::a1; 61 | template 62 | constexpr CoeffType poly_coeffs::a2; 63 | template 64 | constexpr CoeffType poly_coeffs::a3; 65 | template 66 | constexpr CoeffType poly_coeffs::a4; 67 | template 68 | constexpr CoeffType poly_coeffs::a5; 69 | template 70 | constexpr CoeffType poly_coeffs::a6; 71 | template 72 | constexpr CoeffType poly_coeffs::a7; 73 | 74 | template 75 | constexpr inline fixed_point evaluate_polynomial( 76 | fixed_point xf) { 77 | using fp = fixed_point; 78 | 79 | //Use a polynomial min-max approximation to generate the exponential of 80 | //the fractional part. Note that the constant 1 of the polynomial is added later, 81 | //this gives us one more bit of precision here for free 82 | using coeffs = poly_coeffs; 83 | return fp{multiply(xf, (coeffs::a1+fp{multiply(xf, (coeffs::a2+fp{multiply(xf, (coeffs::a3+fp{multiply(xf, (coeffs::a4 84 | +fp{multiply(xf, (coeffs::a5+fp{multiply(xf, (coeffs::a6+fp{multiply(fp{coeffs::a7}, fp{xf})}))}))}))}))}))}))}; 85 | } 86 | 87 | //Computes 2^x - 1 for a number x between 0 and 1, strictly less than 1 88 | //If the exponent is not negative, there is no fractional part, 89 | //so this is always zero 90 | template=0), int> dummy = 0> 91 | inline constexpr make_largest_ufraction> exp2m1_0to1( 92 | fixed_point) { 93 | return make_largest_ufraction>::from_data( 94 | 0); //Cannot construct from 0, since that would be a shift by more than width of type! 95 | } 96 | //for a positive exponent, some work needs to be done 97 | template dummy = 0> 98 | constexpr inline make_largest_ufraction> exp2m1_0to1( 99 | fixed_point x) { 100 | 101 | //Build the type with the same number of bits, all fractional, 102 | //and unsigned. That should be enough to exactly hold enough bits 103 | //to guarantee bit-accurate results 104 | using im = make_largest_ufraction>; 105 | //The intermediate value type 106 | 107 | return evaluate_polynomial(im{x}); //Important: convert the type once, to keep every multiply from costing a cast 108 | } 109 | 110 | template 111 | constexpr inline Rep floor(fixed_point x) { 112 | return Rep { (x.data()) >> -Exponent }; 113 | } 114 | 115 | } 116 | } 117 | 118 | /// Calculates exp2(x), i.e. 2^x 119 | /// \headerfile sg14/fixed_point 120 | /// 121 | /// Accurate to 1LSB for up to 32 bit underlying representation. 122 | /// 123 | /// \tparam x the input value as a fixed_point 124 | /// 125 | /// \return the result of the exponential, in the same representation as x 126 | template 127 | constexpr fixed_point exp2(fixed_point x) { 128 | using namespace _impl::fp; 129 | 130 | using out_type = fixed_point; 131 | // The input type 132 | using im = make_largest_ufraction; 133 | 134 | //Calculate the final result by shifting the fractional part around. 135 | //Remember to add the 1 which is left out to get 1 bit more resolution 136 | return out_type::from_data( 137 | floor(x) <= Exponent ? 138 | typename im::rep{1}//return immediately if the shift would result in all bits being shifted out 139 | : 140 | //Do the shifts manually. Once the branch with shift operators is merged, could use those 141 | (exp2m1_0to1(static_cast(x - floor(x))).data()//Calculate the exponent of the fractional part 142 | >> (-im::exponent + Exponent - floor(x)))//shift it to the right place 143 | + (Rep { 1 } << (floor(x) - Exponent))); //The constant term must be one, to make integer powers correct 144 | } 145 | 146 | } 147 | 148 | #endif /* FIXED_POINT_MATH_H_ */ 149 | --------------------------------------------------------------------------------