├── .github └── workflows │ └── action.yml ├── CMakeLists.txt ├── README.md ├── build └── cb_win │ └── decimal │ ├── decimal.cbp │ └── readme.txt ├── doc └── license.txt ├── include └── decimal.h └── tests ├── decimalTest.cpp ├── decimalTestAbout.cpp ├── decimalTestArithmetic.cpp ├── decimalTestDiv.cpp ├── decimalTestEdgeCases.cpp ├── decimalTestModulo.cpp ├── decimalTestMult.cpp ├── decimalTestMultDiv.cpp ├── decimalTestRoundOth.cpp ├── decimalTestRounding.cpp ├── decimalTestString.cpp ├── decimalTestTypeLevel.cpp ├── decimalTestUtils.cpp ├── decimalTestUtils.h ├── decimalTestWithExponent.cpp └── runner.cpp /.github/workflows/action.yml: -------------------------------------------------------------------------------- 1 | name: decimal_for_cpp 2 | 3 | on: [push] 4 | 5 | jobs: 6 | linux: 7 | runs-on: ${{ matrix.os }} 8 | 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest, ubuntu-20.04] 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Install dependencies 17 | run: | 18 | sudo apt update 19 | sudo apt install -y libboost-all-dev 20 | 21 | - name: Build c++11 22 | run: | 23 | cd ${{ github.workspace }} 24 | rm -rf b 25 | mkdir -p b 26 | cd b 27 | cmake -DCMAKE_CXX_STANDARD=11 ../ 28 | make VERBOSE=1 && ./test_runner 29 | 30 | - name: Build c++14 31 | run: | 32 | cd ${{ github.workspace }} 33 | rm -rf b 34 | mkdir -p b 35 | cd b 36 | cmake -DCMAKE_CXX_STANDARD=14 ../ 37 | make VERBOSE=1 && ./test_runner 38 | 39 | - name: Build c++17 40 | run: | 41 | cd ${{ github.workspace }} 42 | rm -rf b 43 | mkdir -p b 44 | cd b 45 | cmake -DCMAKE_CXX_STANDARD=17 ../ 46 | make VERBOSE=1 && ./test_runner 47 | 48 | macos: 49 | runs-on: ${{ matrix.os }} 50 | 51 | strategy: 52 | matrix: 53 | os: [macos-latest] 54 | 55 | steps: 56 | - uses: actions/checkout@v2 57 | 58 | - name: Install dependencies 59 | run: | 60 | brew install boost 61 | 62 | - name: Update dependencies 63 | run: | 64 | brew upgrade boost cmake 65 | 66 | - name: Build c++11 67 | run: | 68 | cd ${{ github.workspace }} 69 | rm -rf b 70 | mkdir -p b 71 | cd b 72 | cmake -DCMAKE_CXX_STANDARD=11 ../ 73 | make VERBOSE=1 && ./test_runner 74 | 75 | - name: Build c++14 76 | run: | 77 | cd ${{ github.workspace }} 78 | rm -rf b 79 | mkdir -p b 80 | cd b 81 | cmake -DCMAKE_CXX_STANDARD=14 ../ 82 | make VERBOSE=1 && ./test_runner 83 | 84 | - name: Build c++17 85 | run: | 86 | cd ${{ github.workspace }} 87 | rm -rf b 88 | mkdir -p b 89 | cd b 90 | cmake -DCMAKE_CXX_STANDARD=17 ../ 91 | make VERBOSE=1 && ./test_runner 92 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(decimal_for_cpp) 3 | include(CTest) 4 | 5 | set(Boost_USE_STATIC_LIBS OFF) 6 | set(Boost_USE_MULTITHREADED ON) 7 | set(Boost_USE_STATIC_RUNTIME OFF) 8 | 9 | set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard version to use (default is 11)") 10 | #set(CMAKE_CXX_STANDARD 98) 11 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 12 | 13 | if(BUILD_TESTING) 14 | find_package(Boost COMPONENTS unit_test_framework REQUIRED) 15 | 16 | if(Boost_FOUND) 17 | include_directories(${Boost_INCLUDE_DIRS} include) 18 | add_executable(test_runner include/decimal.h 19 | tests/runner.cpp 20 | tests/decimalTest.cpp 21 | tests/decimalTestAbout.cpp 22 | tests/decimalTestArithmetic.cpp 23 | tests/decimalTestDiv.cpp 24 | tests/decimalTestEdgeCases.cpp 25 | tests/decimalTestModulo.cpp 26 | tests/decimalTestMult.cpp 27 | tests/decimalTestMultDiv.cpp 28 | tests/decimalTestRounding.cpp 29 | tests/decimalTestRoundOth.cpp 30 | tests/decimalTestString.cpp 31 | tests/decimalTestTypeLevel.cpp 32 | tests/decimalTestUtils.cpp 33 | tests/decimalTestWithExponent.cpp 34 | tests/decimalTestUtils.h) 35 | target_include_directories(test_runner PRIVATE ${BOOST_INCLUDE_DIRS}) 36 | endif() 37 | 38 | enable_testing() 39 | add_test(test_runner test_runner) 40 | endif() 41 | 42 | install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | Decimal data type support, for COBOL-like fixed-point operations on currency/money values. 3 | 4 | ![decimal_for_cpp](https://github.com/vpiotr/decimal_for_cpp/actions/workflows/action.yml/badge.svg?branch=master) 5 | 6 | Author: Piotr Likus 7 | 8 | Created: 03/01/2011 9 | 10 | Modified: 18/09/2024 11 | 12 | Licence: BSD 13 | 14 | Version: 1.21 15 | 16 | 17 | This data type is designed to perform calculation with on-fly roundings 18 | & to support correct compare function (floating-point compare is 19 | unreliable). 20 | 21 | Values are stored internally using 64-bit integer, so maximum number of 22 | digits is 18. 23 | 24 | Precision is user-defined, so you can use this data type for currency 25 | rates. 26 | 27 | To store decimal in file you can use "unbiased" functions or use stream i/o. 28 | 29 | # Examples 30 | 31 | Example usage: 32 | ```c++ 33 | #include "decimal.h" 34 | 35 | using namespace dec; 36 | using namespace std; 37 | 38 | // the following declares currency variable with 2 decimal points 39 | // initialized with integer value (can be also floating-point) 40 | decimal<2> value(143125); 41 | 42 | // displays: Value #1 is: 143125.00 43 | cout << "Value #1 is: " << value << endl; 44 | 45 | // declare precise value with digits after decimal point 46 | decimal<2> b("0.11"); 47 | 48 | // perform calculations as with any other numeric type 49 | value += b; 50 | 51 | // displays: Value #2 is: 143125.11 52 | cout << "Value #2 is: " << value << endl; 53 | 54 | // automatic rounding performed here 55 | value /= 1000; 56 | 57 | // displays: Value #3 is: 143.13 58 | cout << "Value #3 is: " << value << endl; 59 | 60 | // integer multiplication and division can be used directly in expression 61 | // when integer is on right side 62 | // displays: Value: 143.13 * 2 is: 286.26 63 | cout << "Value: " << value << " * 2 is: " << (value * 2) << endl; 64 | 65 | // to use integer on left side you need to cast it 66 | // displays: Value: 2 * 143.13 is: 286.26 67 | cout << "Value: 2 * " << value << " is: " << (decimal_cast<2>(2) * value) << endl; 68 | 69 | // to use non-integer constants in expressions you need to use decimal_cast 70 | value = value * decimal_cast<2>("3.33") / decimal_cast<2>(333.0); 71 | 72 | // displays: Value #4 is: 1.43 73 | cout << "Value #4 is: " << value << endl; 74 | 75 | // to mix decimals with different precision use decimal_cast 76 | // it will round result automatically 77 | decimal<6> exchangeRate(12.1234); 78 | value = decimal_cast<2>(decimal_cast<6>(value) * exchangeRate); 79 | 80 | // displays: Value #5 is: 17.34 81 | cout << "Value #5 is: " << value << endl; 82 | 83 | // supports optional strong typing, e.g. 84 | // depending on configuration mixing precision can be forbidden 85 | // or handled automatically 86 | decimal<2> d2("12.03"); 87 | decimal<4> d4("123.0103"); 88 | 89 | // compiles always 90 | d2 += d2; 91 | d2 += decimal_cast<2>(d4); 92 | d4 += decimal_cast<4>(d2); 93 | 94 | #if DEC_TYPE_LEVEL >= 2 95 | // potential precision loss 96 | // this will fail to compile if you define DEC_TYPE_LEVEL = 0 or 1 97 | d2 += d4; 98 | #endif 99 | 100 | #if DEC_TYPE_LEVEL >= 1 101 | // (possibly unintentional) mixed precision without type casting 102 | // this will fail to compile if you define DEC_TYPE_LEVEL = 0 103 | d4 += d2; 104 | #endif 105 | 106 | // for default setup displays: mixed d2 = 417.15 107 | cout << "mixed d2 = " << d2 << endl; 108 | // for default setup displays: mixed d4 = 687.2303 109 | cout << "mixed d4 = " << d4 << endl; 110 | 111 | // supports decimal and thousand separator localization 112 | dec::decimal_format format(',', '.'); 113 | 114 | std::string srcText = "315499999999999.98"; 115 | std::string formatted = "315.499.999.999.999,98"; 116 | dec::decimal<2> srcDecimal(srcText); 117 | 118 | BOOST_CHECK_EQUAL(dec::toString(srcDecimal, format), formatted); 119 | 120 | ``` 121 | 122 | # Supported rounding modes: 123 | 124 | * def_round_policy: default rounding (arithmetic) 125 | * null_round_policy: round towards zero = truncate 126 | * half_down_round_policy: round half towards negative infinity 127 | * half_up_round_policy: round half towards positive infinity 128 | * half_even_round_policy: bankers' rounding, convergent rounding, statistician's rounding, Dutch rounding, Gaussian rounding 129 | * ceiling_round_policy: round towards positive infinity 130 | * floor_round_policy: round towards negative infinity 131 | * round_down_round_policy: round towards zero = truncate 132 | * round_up_round_policy: round away from zero 133 | 134 | In order to use one of these rounding modes you need to declare your decimal variable like this: 135 | 136 | dec::decimal<2, half_even_round_policy> a; 137 | 138 | and it will perform required rounding automatically - for example during assignment or arithmetic operations. 139 | 140 | # Testing 141 | 142 | In order to test the library: 143 | 144 | cd ~/tmp 145 | git clone https://github.com/vpiotr/decimal_for_cpp.git 146 | cd decimal_for_cpp 147 | mkdir _build 148 | cd _build 149 | 150 | # to create makefile with test support 151 | cmake .. 152 | # or 153 | cmake -DBUILD_TESTING=ON .. 154 | 155 | # to create makefile without test support (and to avoid Boost unit testing) 156 | cmake -DBUILD_TESTING=OFF .. 157 | 158 | # to build or install library (only required for testing) 159 | make all 160 | 161 | # to execute all test runners 162 | make test 163 | 164 | # to execute specific runner 165 | ./test_runner 166 | 167 | # to list all test cases during runner execution 168 | ./test_runner --log_level=test_suite 169 | 170 | # to execute tests via ctest 171 | ctest -v 172 | 173 | # Other information 174 | For more examples please see \test directory. 175 | 176 | Directory structure: 177 | ``` 178 | \doc - documentation (licence etc.) 179 | \include - headers 180 | \test - unit tests, Boost-based 181 | ``` 182 | 183 | Code documentation can be generated using Doxygen: 184 | http://www.doxygen.org/ 185 | 186 | Tested compilers: 187 | 188 | - VS2019 Community (MSVC++ 14.2) 189 | - gcc 11.4.0 190 | 191 | Uses C++11 by default, define DEC_NO_CPP11 symbol if your compiler does not support this standard. 192 | To use custom namespace, define DEC_NAMESPACE symbol which should contain your target namespace for decimal type. 193 | For full list of configuration options see "Config section" in decimal.h file. 194 | 195 | For list of project contributors, currently open issues or latest version see project site: 196 | https://github.com/vpiotr/decimal_for_cpp 197 | 198 | 199 | -------------------------------------------------------------------------------- /build/cb_win/decimal/decimal.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 54 | 55 | -------------------------------------------------------------------------------- /build/cb_win/decimal/readme.txt: -------------------------------------------------------------------------------- 1 | Project files for unit tests for Code::Blocks + MinGW compiler on Windows. -------------------------------------------------------------------------------- /doc/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2014, Piotr Likus 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Piotr Likus nor the names of his 15 | contributors may be used to endorse or promote products derived from this 16 | software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /include/decimal.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | // Name: decimal.h 3 | // Purpose: Decimal data type support, for COBOL-like fixed-point 4 | // operations on currency values. 5 | // Author: Piotr Likus 6 | // Created: 03/01/2011 7 | // Last change: 18/04/2021 8 | // Version: 1.18 9 | // Licence: BSD 10 | ///////////////////////////////////////////////////////////////////////////// 11 | 12 | #ifndef _DECIMAL_H__ 13 | #define _DECIMAL_H__ 14 | 15 | // ---------------------------------------------------------------------------- 16 | // Description 17 | // ---------------------------------------------------------------------------- 18 | /// \file decimal.h 19 | /// 20 | /// Decimal value type. Use for capital calculations. 21 | /// Note: maximum handled value is: +9,223,372,036,854,775,807 (divided by prec) 22 | /// 23 | /// Sample usage: 24 | /// using namespace dec; 25 | /// decimal<2> value(143125); 26 | /// value = value / decimal_cast<2>(333); 27 | /// cout << "Result is: " << value << endl; 28 | 29 | // ---------------------------------------------------------------------------- 30 | // Config section 31 | // ---------------------------------------------------------------------------- 32 | // - define DEC_EXTERNAL_INT64 if you do not want internal definition of "int64" data type 33 | // in this case define "DEC_INT64" somewhere 34 | // - define DEC_EXTERNAL_ROUND if you do not want internal "round()" function 35 | // - define DEC_CROSS_DOUBLE if you want to use double (instead of xdouble) for cross-conversions 36 | // - define DEC_EXTERNAL_LIMITS to define by yourself DEC_MAX_INT32 37 | // - define DEC_NO_CPP11 if your compiler does not support C++11 38 | // - define DEC_ALLOW_SPACESHIP_OPER as 1 if your compiler supports spaceship operator 39 | // - define DEC_TRIVIAL_DEFAULT_CONSTRUCTIBLE as 1 if you want to make default constructor trivial 40 | // use with caution because default constructor will not initialize the object 41 | // - define DEC_TYPE_LEVEL as 0 for strong typing (same precision required for both arguments), 42 | // as 1 for allowing to mix lower or equal precision types 43 | // as 2 for automatic rounding when different precision is mixed 44 | 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #ifndef DEC_TYPE_LEVEL 52 | #define DEC_TYPE_LEVEL 2 53 | #endif 54 | 55 | // --> include headers for limits and int64_t 56 | 57 | #ifndef DEC_NO_CPP11 58 | #include 59 | #include 60 | 61 | #else 62 | 63 | #ifndef __STDC_LIMIT_MACROS 64 | #define __STDC_LIMIT_MACROS 65 | #endif 66 | 67 | #if defined(__GXX_EXPERIMENTAL_CXX0X) || (__cplusplus >= 201103L) 68 | #include 69 | #else 70 | #include 71 | #endif // defined 72 | 73 | #endif // DEC_NO_CPP11 74 | 75 | #ifdef DEC_NO_CPP11 76 | #define DEC_OVERRIDE 77 | #else 78 | #define DEC_OVERRIDE override 79 | #endif 80 | 81 | #ifdef DEC_NO_CPP11 82 | #define DEC_CONSTEXPR const 83 | #else 84 | #define DEC_CONSTEXPR constexpr 85 | #endif 86 | 87 | #ifdef DEC_NO_CPP11 88 | #define DEC_MOVE(x) (x) 89 | #else 90 | #include 91 | #define DEC_MOVE(x) std::move(x) 92 | #endif 93 | 94 | #if (DEC_ALLOW_SPACESHIP_OPER == 1) && (__cplusplus > 201703L) 95 | #define DEC_USE_SPACESHIP_OPER 1 96 | #else 97 | #undef DEC_USE_SPACESHIP_OPER 98 | #define DEC_USE_SPACESHIP_OPER 0 99 | #endif 100 | 101 | // <-- 102 | 103 | // --> define DEC_MAX_INTxx, DEC_MIN_INTxx if required 104 | 105 | #ifndef DEC_NAMESPACE 106 | #define DEC_NAMESPACE dec 107 | #endif // DEC_NAMESPACE 108 | 109 | #ifndef DEC_EXTERNAL_LIMITS 110 | #ifndef DEC_NO_CPP11 111 | //#define DEC_MAX_INT32 ((std::numeric_limits::max)()) 112 | #define DEC_MAX_INT64 ((std::numeric_limits::max)()) 113 | #define DEC_MIN_INT64 ((std::numeric_limits::min)()) 114 | #else 115 | //#define DEC_MAX_INT32 INT32_MAX 116 | #define DEC_MAX_INT64 INT64_MAX 117 | #define DEC_MIN_INT64 INT64_MIN 118 | #endif // DEC_NO_CPP11 119 | #endif // DEC_EXTERNAL_LIMITS 120 | 121 | // <-- 122 | 123 | namespace DEC_NAMESPACE { 124 | 125 | #ifdef DEC_NO_CPP11 126 | template 127 | struct enable_if_type { 128 | typedef T type; 129 | }; 130 | 131 | template 132 | struct enable_if_type { 133 | }; 134 | 135 | template 136 | struct enable_if: public enable_if_type { 137 | }; 138 | 139 | #define ENABLE_IF dec::enable_if 140 | #else 141 | #define ENABLE_IF std::enable_if 142 | #endif 143 | 144 | 145 | // ---------------------------------------------------------------------------- 146 | // Simple type definitions 147 | // ---------------------------------------------------------------------------- 148 | 149 | // --> define DEC_INT64 if required 150 | #ifndef DEC_EXTERNAL_INT64 151 | #ifndef DEC_NO_CPP11 152 | typedef int64_t DEC_INT64; 153 | #else 154 | #if defined(_MSC_VER) || defined(__BORLANDC__) 155 | typedef signed __int64 DEC_INT64; 156 | #else 157 | typedef signed long long DEC_INT64; 158 | #endif 159 | #endif 160 | #endif // DEC_EXTERNAL_INT64 161 | // <-- 162 | 163 | // --> define DEC_HANDLE_LONG if const long meets ambiguous conversion. 164 | #ifndef DEC_HANDLE_LONG 165 | #if defined(__APPLE__) || defined(__MACH__) 166 | #define DEC_HANDLE_LONG 167 | #endif 168 | #endif // DEC_HANDLE_LONG 169 | // <-- 170 | 171 | #ifdef DEC_NO_CPP11 172 | #define static_assert(a,b) 173 | #endif 174 | 175 | typedef DEC_INT64 int64; 176 | // type for storing currency value internally 177 | typedef int64 dec_storage_t; 178 | typedef unsigned int uint; 179 | // xdouble is an "extended double" - can be long double, __float128, _Quad - as you wish 180 | typedef long double xdouble; 181 | 182 | #ifdef DEC_CROSS_DOUBLE 183 | typedef double cross_float; 184 | #else 185 | typedef xdouble cross_float; 186 | #endif 187 | 188 | // ---------------------------------------------------------------------------- 189 | // Forward class definitions 190 | // ---------------------------------------------------------------------------- 191 | class basic_decimal_format; 192 | 193 | // ---------------------------------------------------------------------------- 194 | // Constants 195 | // ---------------------------------------------------------------------------- 196 | enum { 197 | max_decimal_points = 18 198 | }; 199 | 200 | // ---------------------------------------------------------------------------- 201 | // Class definitions 202 | // ---------------------------------------------------------------------------- 203 | template struct DecimalFactor { 204 | static DEC_CONSTEXPR int64 value = 10 * DecimalFactor::value; 205 | }; 206 | 207 | template<> struct DecimalFactor<0> { 208 | static DEC_CONSTEXPR int64 value = 1; 209 | }; 210 | 211 | template<> struct DecimalFactor<1> { 212 | static DEC_CONSTEXPR int64 value = 10; 213 | }; 214 | 215 | template struct DecimalFactorDiff_impl { 216 | static DEC_CONSTEXPR int64 value = DecimalFactor::value; 217 | }; 218 | 219 | template struct DecimalFactorDiff_impl { 220 | static DEC_CONSTEXPR int64 value = INT64_MIN; 221 | }; 222 | 223 | template struct DecimalFactorDiff { 224 | static DEC_CONSTEXPR int64 value = DecimalFactorDiff_impl= 0>::value; 225 | }; 226 | 227 | #ifndef DEC_EXTERNAL_ROUND 228 | 229 | // round floating point value and convert to int64 230 | template 231 | inline int64 round(T value) { 232 | T val1; 233 | 234 | if (value < 0.0) { 235 | val1 = value - 0.5; 236 | } else { 237 | val1 = value + 0.5; 238 | } 239 | int64 intPart = static_cast(val1); 240 | 241 | return intPart; 242 | } 243 | 244 | // calculate output = round(a / b), where output, a, b are int64 245 | inline bool div_rounded(int64 &output, int64 a, int64 b) { 246 | int64 divisorCorr = std::abs(b) / 2; 247 | if (a >= 0) { 248 | if (DEC_MAX_INT64 - a >= divisorCorr) { 249 | output = (a + divisorCorr) / b; 250 | return true; 251 | } else { 252 | const int64 i = a / b; 253 | const int64 r = a - i * b; 254 | if (r < divisorCorr) { 255 | output = i; 256 | return true; 257 | } 258 | } 259 | } else { 260 | if (-(DEC_MIN_INT64 - a) >= divisorCorr) { 261 | output = (a - divisorCorr) / b; 262 | return true; 263 | } else { 264 | const int64 i = a / b; 265 | const int64 r = a - i * b; 266 | if (r < divisorCorr) { 267 | output = i; 268 | return true; 269 | } 270 | } 271 | } 272 | 273 | output = 0; 274 | return false; 275 | } 276 | 277 | #endif // DEC_EXTERNAL_ROUND 278 | 279 | template 280 | class dec_utils { 281 | public: 282 | // result = (value1 * value2) / divisor 283 | inline static int64 multDiv(const int64 value1, const int64 value2, 284 | int64 divisor) { 285 | 286 | if (value1 == 0 || value2 == 0) { 287 | return 0; 288 | } 289 | 290 | if (divisor == 1) { 291 | return value1 * value2; 292 | } 293 | 294 | if (value1 == 1) { 295 | int64 result; 296 | if (RoundPolicy::div_rounded(result, value2, divisor)) { 297 | return result; 298 | } 299 | } 300 | 301 | if (value2 == 1) { 302 | int64 result; 303 | if (RoundPolicy::div_rounded(result, value1, divisor)) { 304 | return result; 305 | } 306 | } 307 | 308 | // we don't check for division by zero, the caller should - the next line will throw. 309 | const int64 value1int = value1 / divisor; 310 | int64 value1dec = value1 % divisor; 311 | const int64 value2int = value2 / divisor; 312 | int64 value2dec = value2 % divisor; 313 | 314 | int64 result = value1 * value2int + value1int * value2dec; 315 | 316 | if (value1dec == 0 || value2dec == 0) { 317 | return result; 318 | } 319 | 320 | if (!isMultOverflow(value1dec, value2dec)) { // no overflow 321 | int64 resDecPart = value1dec * value2dec; 322 | if (!RoundPolicy::div_rounded(resDecPart, resDecPart, divisor)) 323 | resDecPart = 0; 324 | result += resDecPart; 325 | return result; 326 | } 327 | 328 | // minimize value1 & divisor 329 | { 330 | int64 c = gcd(value1dec, divisor); 331 | if (c != 1) { 332 | value1dec /= c; 333 | divisor /= c; 334 | } 335 | 336 | // minimize value2 & divisor 337 | c = gcd(value2dec, divisor); 338 | if (c != 1) { 339 | value2dec /= c; 340 | divisor /= c; 341 | } 342 | } 343 | 344 | if (!isMultOverflow(value1dec, value2dec)) { // no overflow 345 | int64 resDecPart = value1dec * value2dec; 346 | if (RoundPolicy::div_rounded(resDecPart, resDecPart, divisor)) { 347 | result += resDecPart; 348 | return result; 349 | } 350 | } 351 | 352 | // overflow can occur - use less precise version 353 | result += RoundPolicy::round( 354 | static_cast(value1dec) 355 | * static_cast(value2dec) 356 | / static_cast(divisor)); 357 | return result; 358 | } 359 | 360 | static bool isMultOverflow(const int64 value1, const int64 value2) { 361 | if (value1 == 0 || value2 == 0) { 362 | return false; 363 | } 364 | 365 | if ((value1 < 0) != (value2 < 0)) { // different sign 366 | if (value1 == DEC_MIN_INT64) { 367 | return value2 > 1; 368 | } else if (value2 == DEC_MIN_INT64) { 369 | return value1 > 1; 370 | } 371 | if (value1 < 0) { 372 | return isMultOverflow(-value1, value2); 373 | } 374 | if (value2 < 0) { 375 | return isMultOverflow(value1, -value2); 376 | } 377 | } else if (value1 < 0 && value2 < 0) { 378 | if (value1 == DEC_MIN_INT64) { 379 | return value2 < -1; 380 | } else if (value2 == DEC_MIN_INT64) { 381 | return value1 < -1; 382 | } 383 | return isMultOverflow(-value1, -value2); 384 | } 385 | 386 | return (value1 > DEC_MAX_INT64 / value2); 387 | } 388 | 389 | static int64 pow10(int n) { 390 | static const int64 decimalFactorTable[] = { 1, 10, 100, 1000, 10000, 391 | 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 392 | 100000000000, 1000000000000, 10000000000000, 100000000000000, 393 | 1000000000000000, 10000000000000000, 100000000000000000, 394 | 1000000000000000000 }; 395 | 396 | if (n >= 0 && n <= max_decimal_points) { 397 | return decimalFactorTable[n]; 398 | } else { 399 | return 0; 400 | } 401 | } 402 | 403 | template 404 | static int64 trunc(T value) { 405 | return static_cast(value); 406 | } 407 | 408 | private: 409 | // calculate greatest common divisor 410 | static int64 gcd(int64 a, int64 b) { 411 | int64 c; 412 | while (a != 0) { 413 | c = a; 414 | a = b % a; 415 | b = c; 416 | } 417 | return b; 418 | } 419 | 420 | }; 421 | 422 | // no-rounding policy (decimal places stripped) 423 | class null_round_policy { 424 | public: 425 | template 426 | static int64 round(T value) { 427 | return static_cast(value); 428 | } 429 | 430 | static bool div_rounded(int64 &output, int64 a, int64 b) { 431 | output = a / b; 432 | return true; 433 | } 434 | }; 435 | 436 | // default rounding policy - arithmetic, to nearest integer 437 | class def_round_policy { 438 | public: 439 | template 440 | static int64 round(T value) { 441 | return DEC_NAMESPACE::round(value); 442 | } 443 | 444 | static bool div_rounded(int64 &output, int64 a, int64 b) { 445 | return DEC_NAMESPACE::div_rounded(output, a, b); 446 | } 447 | }; 448 | 449 | class half_down_round_policy { 450 | public: 451 | template 452 | static int64 round(T value) { 453 | T val1; 454 | T decimals; 455 | 456 | if (value >= 0.0) { 457 | decimals = value - floor(value); 458 | if (decimals > 0.5) { 459 | val1 = ceil(value); 460 | } else { 461 | val1 = value; 462 | } 463 | } else { 464 | decimals = std::abs(value + floor(std::abs(value))); 465 | if (decimals < 0.5) { 466 | val1 = ceil(value); 467 | } else { 468 | val1 = value; 469 | } 470 | } 471 | 472 | return static_cast(floor(val1)); 473 | } 474 | 475 | static bool div_rounded(int64 &output, int64 a, int64 b) { 476 | int64 divisorCorr = std::abs(b) / 2; 477 | int64 remainder = std::abs(a) % std::abs(b); 478 | 479 | if (a >= 0) { 480 | if (DEC_MAX_INT64 - a >= divisorCorr) { 481 | if (remainder > divisorCorr) { 482 | output = (a + divisorCorr) / b; 483 | } else { 484 | output = a / b; 485 | } 486 | return true; 487 | } 488 | } else { 489 | if (-(DEC_MIN_INT64 - a) >= divisorCorr) { 490 | output = (a - divisorCorr) / b; 491 | return true; 492 | } 493 | } 494 | 495 | output = 0; 496 | return false; 497 | } 498 | }; 499 | 500 | class half_up_round_policy { 501 | public: 502 | template 503 | static int64 round(T value) { 504 | T val1; 505 | T decimals; 506 | 507 | if (value >= 0.0) { 508 | decimals = value - floor(value); 509 | if (decimals >= 0.5) { 510 | val1 = ceil(value); 511 | } else { 512 | val1 = value; 513 | } 514 | } else { 515 | decimals = std::abs(value + floor(std::abs(value))); 516 | if (decimals <= 0.5) { 517 | val1 = ceil(value); 518 | } else { 519 | val1 = value; 520 | } 521 | } 522 | 523 | return static_cast(floor(val1)); 524 | } 525 | 526 | static bool div_rounded(int64 &output, int64 a, int64 b) { 527 | int64 divisorCorr = std::abs(b) / 2; 528 | int64 remainder = std::abs(a) % std::abs(b); 529 | 530 | if (a >= 0) { 531 | if (DEC_MAX_INT64 - a >= divisorCorr) { 532 | if (remainder >= divisorCorr) { 533 | output = (a + divisorCorr) / b; 534 | } else { 535 | output = a / b; 536 | } 537 | return true; 538 | } 539 | } else { 540 | if (-(DEC_MIN_INT64 - a) >= divisorCorr) { 541 | if (remainder < divisorCorr) { 542 | output = (a - remainder) / b; 543 | } else if (remainder == divisorCorr) { 544 | output = (a + divisorCorr) / b; 545 | } else { 546 | output = (a + remainder - std::abs(b)) / b; 547 | } 548 | return true; 549 | } 550 | } 551 | 552 | output = 0; 553 | return false; 554 | } 555 | }; 556 | 557 | // bankers' rounding 558 | class half_even_round_policy { 559 | public: 560 | template 561 | static int64 round(T value) { 562 | T val1; 563 | T decimals; 564 | 565 | if (value >= 0.0) { 566 | decimals = value - floor(value); 567 | if (decimals > 0.5) { 568 | val1 = ceil(value); 569 | } else if (decimals < 0.5) { 570 | val1 = floor(value); 571 | } else { 572 | bool is_even = (static_cast(value - decimals) % 2 == 0); 573 | if (is_even) { 574 | val1 = floor(value); 575 | } else { 576 | val1 = ceil(value); 577 | } 578 | } 579 | } else { 580 | decimals = std::abs(value + floor(std::abs(value))); 581 | if (decimals > 0.5) { 582 | val1 = floor(value); 583 | } else if (decimals < 0.5) { 584 | val1 = ceil(value); 585 | } else { 586 | bool is_even = (static_cast(value + decimals) % 2 == 0); 587 | if (is_even) { 588 | val1 = ceil(value); 589 | } else { 590 | val1 = floor(value); 591 | } 592 | } 593 | } 594 | 595 | return static_cast(val1); 596 | } 597 | 598 | static bool div_rounded(int64 &output, int64 a, int64 b) { 599 | int64 divisorDiv2 = std::abs(b) / 2; 600 | int64 remainder = std::abs(a) % std::abs(b); 601 | 602 | if (remainder == 0) { 603 | output = a / b; 604 | } else { 605 | if (a >= 0) { 606 | 607 | if (remainder > divisorDiv2) { 608 | output = (a - remainder + std::abs(b)) / b; 609 | } else if (remainder < divisorDiv2) { 610 | output = (a - remainder) / b; 611 | } else { 612 | bool is_even = std::abs(a / b) % 2 == 0; 613 | if (is_even) { 614 | output = a / b; 615 | } else { 616 | output = (a - remainder + std::abs(b)) / b; 617 | } 618 | } 619 | } else { 620 | // negative value 621 | if (remainder > divisorDiv2) { 622 | output = (a + remainder - std::abs(b)) / b; 623 | } else if (remainder < divisorDiv2) { 624 | output = (a + remainder) / b; 625 | } else { 626 | bool is_even = std::abs(a / b) % 2 == 0; 627 | if (is_even) { 628 | output = a / b; 629 | } else { 630 | output = (a + remainder - std::abs(b)) / b; 631 | } 632 | } 633 | } 634 | } 635 | 636 | return true; 637 | } 638 | }; 639 | 640 | // round towards +infinity 641 | class ceiling_round_policy { 642 | public: 643 | template 644 | static int64 round(T value) { 645 | return static_cast(ceil(value)); 646 | } 647 | 648 | static bool div_rounded(int64 &output, int64 a, int64 b) { 649 | int64 remainder = std::abs(a) % std::abs(b); 650 | if (remainder == 0) { 651 | output = a / b; 652 | } else { 653 | if (a >= 0) { 654 | output = (a + std::abs(b)) / b; 655 | } else { 656 | output = a / b; 657 | } 658 | } 659 | return true; 660 | } 661 | }; 662 | 663 | // round towards -infinity 664 | class floor_round_policy { 665 | public: 666 | template 667 | static int64 round(T value) { 668 | return static_cast(floor(value)); 669 | } 670 | 671 | static bool div_rounded(int64 &output, int64 a, int64 b) { 672 | int64 remainder = std::abs(a) % std::abs(b); 673 | if (remainder == 0) { 674 | output = a / b; 675 | } else { 676 | if (a >= 0) { 677 | output = (a - remainder) / b; 678 | } else { 679 | output = (a + remainder - std::abs(b)) / b; 680 | } 681 | } 682 | return true; 683 | } 684 | }; 685 | 686 | // round towards zero = truncate 687 | class round_down_round_policy: public null_round_policy { 688 | }; 689 | 690 | // round away from zero 691 | class round_up_round_policy { 692 | public: 693 | template 694 | static int64 round(T value) { 695 | if (value >= 0.0) { 696 | return static_cast(ceil(value)); 697 | } else { 698 | return static_cast(floor(value)); 699 | } 700 | } 701 | 702 | static bool div_rounded(int64 &output, int64 a, int64 b) { 703 | int64 remainder = std::abs(a) % std::abs(b); 704 | if (remainder == 0) { 705 | output = a / b; 706 | } else { 707 | if (a >= 0) { 708 | output = (a + std::abs(b)) / b; 709 | } else { 710 | output = (a - std::abs(b)) / b; 711 | } 712 | } 713 | return true; 714 | } 715 | }; 716 | 717 | template 718 | class decimal { 719 | public: 720 | typedef dec_storage_t raw_data_t; 721 | enum { 722 | decimal_points = Prec 723 | }; 724 | 725 | #ifdef DEC_NO_CPP11 726 | #ifdef DEC_TRIVIAL_DEFAULT_CONSTRUCTIBLE 727 | decimal() { 728 | } 729 | #else 730 | decimal() { 731 | init(0); 732 | } 733 | #endif 734 | decimal(const decimal &src) { 735 | init(src); 736 | } 737 | #else 738 | #ifdef DEC_TRIVIAL_DEFAULT_CONSTRUCTIBLE 739 | decimal() noexcept = default; 740 | #else 741 | decimal() noexcept : m_value(0) {} 742 | #endif 743 | decimal(const decimal &src) = default; 744 | #endif 745 | explicit decimal(uint value) { 746 | init(value); 747 | } 748 | explicit decimal(int value) { 749 | init(value); 750 | } 751 | #ifdef DEC_HANDLE_LONG 752 | explicit decimal(long int value) { 753 | init(value); 754 | } 755 | #endif 756 | explicit decimal(int64 value) { 757 | init(value); 758 | } 759 | explicit decimal(xdouble value) { 760 | init(value); 761 | } 762 | explicit decimal(double value) { 763 | init(value); 764 | } 765 | explicit decimal(float value) { 766 | init(value); 767 | } 768 | explicit decimal(int64 value, int64 precFactor) { 769 | initWithPrec(value, precFactor); 770 | } 771 | explicit decimal(const std::string &value) { 772 | fromString(value, *this); 773 | } 774 | 775 | explicit decimal(const std::string &value, const basic_decimal_format &format) { 776 | fromString(value, format, *this); 777 | } 778 | 779 | #ifdef DEC_NO_CPP11 780 | ~decimal() { 781 | } 782 | #else 783 | ~decimal() = default; 784 | #endif 785 | 786 | static int64 getPrecFactor() { 787 | return DecimalFactor::value; 788 | } 789 | static int getDecimalPoints() { 790 | return Prec; 791 | } 792 | 793 | #ifdef DEC_NO_CPP11 794 | decimal & operator=(const decimal &rhs) { 795 | if (&rhs != this) 796 | m_value = rhs.m_value; 797 | return *this; 798 | } 799 | #else 800 | decimal & operator=(const decimal &rhs) = default; 801 | #endif 802 | 803 | #if DEC_TYPE_LEVEL == 1 804 | template 805 | typename ENABLE_IF= Prec2, decimal>::type 806 | & operator=(const decimal &rhs) { 807 | m_value = rhs.getUnbiased() * DecimalFactorDiff::value; 808 | return *this; 809 | } 810 | #elif DEC_TYPE_LEVEL > 1 811 | template 812 | decimal & operator=(const decimal &rhs) { 813 | if (Prec2 > Prec) { 814 | RoundPolicy::div_rounded(m_value, rhs.getUnbiased(), 815 | DecimalFactorDiff::value); 816 | } else { 817 | m_value = rhs.getUnbiased() 818 | * DecimalFactorDiff::value; 819 | } 820 | return *this; 821 | } 822 | #endif 823 | 824 | decimal & operator=(int64 rhs) { 825 | m_value = DecimalFactor::value * rhs; 826 | return *this; 827 | } 828 | 829 | decimal & operator=(int rhs) { 830 | m_value = DecimalFactor::value * rhs; 831 | return *this; 832 | } 833 | 834 | decimal & operator=(double rhs) { 835 | m_value = fpToStorage(rhs); 836 | return *this; 837 | } 838 | 839 | decimal & operator=(xdouble rhs) { 840 | m_value = fpToStorage(rhs); 841 | return *this; 842 | } 843 | 844 | template 845 | bool operator==(const T &rhs) const { 846 | return (*this == static_cast(rhs)); 847 | } 848 | 849 | template 850 | bool operator!=(const T &rhs) const { 851 | return !(*this == rhs); 852 | } 853 | 854 | #if DEC_USE_SPACESHIP_OPER 855 | template 856 | auto operator<=>(const T &rhs) const { 857 | return (*this <=> static_cast(rhs)); 858 | } 859 | #else 860 | template 861 | bool operator<(const T &rhs) const { 862 | return (*this < static_cast(rhs)); 863 | } 864 | 865 | template 866 | bool operator<=(const T &rhs) const { 867 | return (*this <= static_cast(rhs)); 868 | } 869 | 870 | template 871 | bool operator>(const T &rhs) const { 872 | return (*this > static_cast(rhs)); 873 | } 874 | 875 | template 876 | bool operator>=(const T &rhs) const { 877 | return (*this >= static_cast(rhs)); 878 | } 879 | #endif 880 | 881 | bool operator==(const decimal &rhs) const { 882 | return (m_value == rhs.m_value); 883 | } 884 | 885 | bool operator!=(const decimal &rhs) const { 886 | return !(*this == rhs); 887 | } 888 | 889 | #if DEC_USE_SPACESHIP_OPER 890 | auto operator<=>(const decimal &rhs) const { 891 | return m_value <=> rhs.m_value; 892 | } 893 | #else 894 | 895 | bool operator<(const decimal &rhs) const { 896 | return (m_value < rhs.m_value); 897 | } 898 | 899 | bool operator<=(const decimal &rhs) const { 900 | return (m_value <= rhs.m_value); 901 | } 902 | 903 | bool operator>(const decimal &rhs) const { 904 | return (m_value > rhs.m_value); 905 | } 906 | 907 | bool operator>=(const decimal &rhs) const { 908 | return (m_value >= rhs.m_value); 909 | } 910 | #endif 911 | 912 | template 913 | const decimal operator+(const T &rhs) const { 914 | return *this + static_cast(rhs); 915 | } 916 | 917 | const decimal operator+(const decimal &rhs) const { 918 | decimal result = *this; 919 | result.m_value += rhs.m_value; 920 | return result; 921 | } 922 | 923 | #if DEC_TYPE_LEVEL == 1 924 | template 925 | const typename ENABLE_IF= Prec2, decimal>::type 926 | operator+(const decimal &rhs) const { 927 | decimal result = *this; 928 | result.m_value += rhs.getUnbiased() * DecimalFactorDiff::value; 929 | return result; 930 | } 931 | #elif DEC_TYPE_LEVEL > 1 932 | template 933 | const decimal operator+(const decimal &rhs) const { 934 | decimal result = *this; 935 | if (Prec2 > Prec) { 936 | int64 val; 937 | RoundPolicy::div_rounded(val, rhs.getUnbiased(), 938 | DecimalFactorDiff::value); 939 | result.m_value += val; 940 | } else { 941 | result.m_value += rhs.getUnbiased() 942 | * DecimalFactorDiff::value; 943 | } 944 | 945 | return result; 946 | } 947 | #endif 948 | 949 | template 950 | decimal & operator+=(const T &rhs) { 951 | *this += static_cast(rhs); 952 | return *this; 953 | } 954 | 955 | decimal & operator+=(const decimal &rhs) { 956 | m_value += rhs.m_value; 957 | return *this; 958 | } 959 | 960 | #if DEC_TYPE_LEVEL == 1 961 | template 962 | typename ENABLE_IF= Prec2, decimal>::type 963 | & operator+=(const decimal &rhs) { 964 | m_value += rhs.getUnbiased() * DecimalFactorDiff::value; 965 | return *this; 966 | } 967 | #elif DEC_TYPE_LEVEL > 1 968 | template 969 | decimal & operator+=(const decimal &rhs) { 970 | if (Prec2 > Prec) { 971 | int64 val; 972 | RoundPolicy::div_rounded(val, rhs.getUnbiased(), 973 | DecimalFactorDiff::value); 974 | m_value += val; 975 | } else { 976 | m_value += rhs.getUnbiased() 977 | * DecimalFactorDiff::value; 978 | } 979 | 980 | return *this; 981 | } 982 | #endif 983 | 984 | const decimal operator+() const { 985 | return *this; 986 | } 987 | 988 | const decimal operator-() const { 989 | decimal result = *this; 990 | result.m_value = -result.m_value; 991 | return result; 992 | } 993 | 994 | template 995 | const decimal operator-(const T &rhs) const { 996 | return *this - static_cast(rhs); 997 | } 998 | 999 | const decimal operator-(const decimal &rhs) const { 1000 | decimal result = *this; 1001 | result.m_value -= rhs.m_value; 1002 | return result; 1003 | } 1004 | 1005 | #if DEC_TYPE_LEVEL == 1 1006 | template 1007 | const typename ENABLE_IF= Prec2, decimal>::type 1008 | operator-(const decimal &rhs) const { 1009 | decimal result = *this; 1010 | result.m_value -= rhs.getUnbiased() * DecimalFactorDiff::value; 1011 | return result; 1012 | } 1013 | #elif DEC_TYPE_LEVEL > 1 1014 | template 1015 | const decimal operator-(const decimal &rhs) const { 1016 | decimal result = *this; 1017 | if (Prec2 > Prec) { 1018 | int64 val; 1019 | RoundPolicy::div_rounded(val, rhs.getUnbiased(), 1020 | DecimalFactorDiff::value); 1021 | result.m_value -= val; 1022 | } else { 1023 | result.m_value -= rhs.getUnbiased() 1024 | * DecimalFactorDiff::value; 1025 | } 1026 | 1027 | return result; 1028 | } 1029 | #endif 1030 | 1031 | template 1032 | decimal & operator-=(const T &rhs) { 1033 | *this -= static_cast(rhs); 1034 | return *this; 1035 | } 1036 | 1037 | decimal & operator-=(const decimal &rhs) { 1038 | m_value -= rhs.m_value; 1039 | return *this; 1040 | } 1041 | 1042 | #if DEC_TYPE_LEVEL == 1 1043 | template 1044 | typename ENABLE_IF= Prec2, decimal>::type 1045 | & operator-=(const decimal &rhs) { 1046 | m_value -= rhs.getUnbiased() * DecimalFactorDiff::value; 1047 | return *this; 1048 | } 1049 | #elif DEC_TYPE_LEVEL > 1 1050 | template 1051 | decimal & operator-=(const decimal &rhs) { 1052 | if (Prec2 > Prec) { 1053 | int64 val; 1054 | RoundPolicy::div_rounded(val, rhs.getUnbiased(), 1055 | DecimalFactorDiff::value); 1056 | m_value -= val; 1057 | } else { 1058 | m_value -= rhs.getUnbiased() 1059 | * DecimalFactorDiff::value; 1060 | } 1061 | 1062 | return *this; 1063 | } 1064 | #endif 1065 | 1066 | template 1067 | const decimal operator*(const T &rhs) const { 1068 | return *this * static_cast(rhs); 1069 | } 1070 | 1071 | const decimal operator*(const decimal &rhs) const { 1072 | decimal result = *this; 1073 | result.m_value = dec_utils::multDiv(result.m_value, 1074 | rhs.m_value, DecimalFactor::value); 1075 | return result; 1076 | } 1077 | 1078 | #if DEC_TYPE_LEVEL == 1 1079 | template 1080 | const typename ENABLE_IF= Prec2, decimal>::type 1081 | operator*(const decimal& rhs) const { 1082 | decimal result = *this; 1083 | result.m_value = dec_utils::multDiv(result.m_value, 1084 | rhs.getUnbiased(), DecimalFactor::value); 1085 | return result; 1086 | } 1087 | #elif DEC_TYPE_LEVEL > 1 1088 | template 1089 | const decimal operator*(const decimal& rhs) const { 1090 | decimal result = *this; 1091 | result.m_value = dec_utils::multDiv(result.m_value, 1092 | rhs.getUnbiased(), DecimalFactor::value); 1093 | return result; 1094 | } 1095 | #endif 1096 | 1097 | template 1098 | decimal & operator*=(const T &rhs) { 1099 | *this *= static_cast(rhs); 1100 | return *this; 1101 | } 1102 | 1103 | decimal & operator*=(const decimal &rhs) { 1104 | m_value = dec_utils::multDiv(m_value, rhs.m_value, 1105 | DecimalFactor::value); 1106 | return *this; 1107 | } 1108 | 1109 | #if DEC_TYPE_LEVEL == 1 1110 | template 1111 | typename ENABLE_IF= Prec2, decimal>::type 1112 | & operator*=(const decimal& rhs) { 1113 | m_value = dec_utils::multDiv(m_value, rhs.getUnbiased(), 1114 | DecimalFactor::value); 1115 | return *this; 1116 | } 1117 | #elif DEC_TYPE_LEVEL > 1 1118 | template 1119 | decimal & operator*=(const decimal& rhs) { 1120 | m_value = dec_utils::multDiv(m_value, rhs.getUnbiased(), 1121 | DecimalFactor::value); 1122 | return *this; 1123 | } 1124 | #endif 1125 | 1126 | template 1127 | const decimal operator/(const T &rhs) const { 1128 | return *this / static_cast(rhs); 1129 | } 1130 | 1131 | const decimal operator/(const decimal &rhs) const { 1132 | decimal result = *this; 1133 | //result.m_value = (result.m_value * DecimalFactor::value) / rhs.m_value; 1134 | result.m_value = dec_utils::multDiv(result.m_value, 1135 | DecimalFactor::value, rhs.m_value); 1136 | 1137 | return result; 1138 | } 1139 | 1140 | #if DEC_TYPE_LEVEL == 1 1141 | template 1142 | const typename ENABLE_IF= Prec2, decimal>::type 1143 | operator/(const decimal& rhs) const { 1144 | decimal result = *this; 1145 | result.m_value = dec_utils::multDiv(result.m_value, 1146 | DecimalFactor::value, rhs.getUnbiased()); 1147 | return result; 1148 | } 1149 | #elif DEC_TYPE_LEVEL > 1 1150 | template 1151 | const decimal operator/(const decimal& rhs) const { 1152 | decimal result = *this; 1153 | result.m_value = dec_utils::multDiv(result.m_value, 1154 | DecimalFactor::value, rhs.getUnbiased()); 1155 | return result; 1156 | } 1157 | #endif 1158 | 1159 | template 1160 | decimal & operator/=(const T &rhs) { 1161 | *this /= static_cast(rhs); 1162 | return *this; 1163 | } 1164 | 1165 | decimal & operator/=(const decimal &rhs) { 1166 | //m_value = (m_value * DecimalFactor::value) / rhs.m_value; 1167 | m_value = dec_utils::multDiv(m_value, 1168 | DecimalFactor::value, rhs.m_value); 1169 | 1170 | return *this; 1171 | } 1172 | 1173 | #if DEC_TYPE_LEVEL == 1 1174 | template 1175 | typename ENABLE_IF= Prec2, decimal>::type 1176 | & operator/=(const decimal &rhs) { 1177 | m_value = dec_utils::multDiv(m_value, 1178 | DecimalFactor::value, rhs.getUnbiased()); 1179 | 1180 | return *this; 1181 | } 1182 | #elif DEC_TYPE_LEVEL > 1 1183 | template 1184 | decimal & operator/=(const decimal &rhs) { 1185 | m_value = dec_utils::multDiv(m_value, 1186 | DecimalFactor::value, rhs.getUnbiased()); 1187 | 1188 | return *this; 1189 | } 1190 | #endif 1191 | 1192 | template 1193 | const decimal operator%(T n) const { 1194 | return *this % static_cast(n); 1195 | } 1196 | 1197 | template 1198 | decimal & operator%=(T rhs) { 1199 | *this %= static_cast(rhs); 1200 | return *this; 1201 | } 1202 | 1203 | const decimal operator%(const decimal &rhs) const { 1204 | int64 resultPayload; 1205 | resultPayload = this->m_value; 1206 | resultPayload %= rhs.m_value; 1207 | decimal result; 1208 | result.m_value = resultPayload; 1209 | return result; 1210 | } 1211 | 1212 | decimal & operator%=(const decimal &rhs) { 1213 | int64 resultPayload; 1214 | resultPayload = this->m_value; 1215 | resultPayload %= rhs.m_value; 1216 | this->m_value = resultPayload; 1217 | return *this; 1218 | } 1219 | 1220 | #if DEC_TYPE_LEVEL >= 1 1221 | template 1222 | typename ENABLE_IF= Prec2, decimal>::type 1223 | operator%(const decimal &rhs) const { 1224 | int64 rhsInThisPrec = rhs.getUnbiased() * DecimalFactorDiff::value; 1225 | int64 resultPayload = this->m_value; 1226 | resultPayload %= rhsInThisPrec; 1227 | decimal result; 1228 | result.m_value = resultPayload; 1229 | return result; 1230 | } 1231 | 1232 | template 1233 | typename ENABLE_IF= Prec2, decimal &>::type 1234 | operator%=(const decimal &rhs) { 1235 | int64 rhsInThisPrec = rhs.getUnbiased() * DecimalFactorDiff::value; 1236 | int64 resultPayload = this->m_value; 1237 | resultPayload %= rhsInThisPrec; 1238 | this->m_value = resultPayload; 1239 | return *this; 1240 | } 1241 | #endif 1242 | 1243 | #if DEC_TYPE_LEVEL > 1 1244 | template 1245 | typename ENABLE_IF::type 1246 | operator%(const decimal &rhs) const { 1247 | int64 thisInRhsPrec = m_value * DecimalFactorDiff::value; 1248 | int64 resultPayload = thisInRhsPrec % rhs.getUnbiased(); 1249 | resultPayload /= DecimalFactorDiff::value; 1250 | decimal result; 1251 | result.m_value = resultPayload; 1252 | return result; 1253 | } 1254 | 1255 | template 1256 | typename ENABLE_IF::type 1257 | operator%=(const decimal &rhs) { 1258 | int64 thisInRhsPrec = m_value * DecimalFactorDiff::value; 1259 | int64 resultPayload = thisInRhsPrec % rhs.getUnbiased(); 1260 | resultPayload /= DecimalFactorDiff::value; 1261 | this->m_value = resultPayload; 1262 | return *this; 1263 | } 1264 | #endif 1265 | 1266 | /// Returns integer indicating sign of value 1267 | /// -1 if value is < 0 1268 | /// +1 if value is > 0 1269 | /// 0 if value is 0 1270 | int sign() const { 1271 | return (m_value > 0) ? 1 : ((m_value < 0) ? -1 : 0); 1272 | } 1273 | 1274 | double getAsDouble() const { 1275 | return static_cast(m_value) / getPrecFactorDouble(); 1276 | } 1277 | 1278 | void setAsDouble(double value) { 1279 | m_value = fpToStorage(value); 1280 | } 1281 | 1282 | xdouble getAsXDouble() const { 1283 | return static_cast(m_value) / getPrecFactorXDouble(); 1284 | } 1285 | 1286 | void setAsXDouble(xdouble value) { 1287 | m_value = fpToStorage(value); 1288 | } 1289 | 1290 | // returns integer value = real_value * (10 ^ precision) 1291 | // use to load/store decimal value in external memory 1292 | int64 getUnbiased() const { 1293 | return m_value; 1294 | } 1295 | void setUnbiased(int64 value) { 1296 | m_value = value; 1297 | } 1298 | 1299 | decimal abs() const { 1300 | if (m_value >= 0) 1301 | return *this; 1302 | else 1303 | return (decimal(0) - *this); 1304 | } 1305 | 1306 | decimal trunc() const { 1307 | int64 beforeValue, afterValue; 1308 | afterValue = m_value % DecimalFactor::value; 1309 | beforeValue = (m_value - afterValue); 1310 | decimal result; 1311 | result.m_value = beforeValue; 1312 | return result; 1313 | } 1314 | 1315 | decimal floor() const { 1316 | int64 beforeValue, afterValue; 1317 | afterValue = m_value % DecimalFactor::value; 1318 | beforeValue = (m_value - afterValue); 1319 | 1320 | if (afterValue < 0) beforeValue -= DecimalFactor::value; 1321 | 1322 | decimal result; 1323 | result.m_value = beforeValue; 1324 | return result; 1325 | } 1326 | 1327 | decimal ceil() const { 1328 | int64 beforeValue, afterValue; 1329 | afterValue = m_value % DecimalFactor::value; 1330 | beforeValue = (m_value - afterValue); 1331 | 1332 | if (afterValue > 0) beforeValue += DecimalFactor::value; 1333 | decimal result; 1334 | result.m_value = beforeValue; 1335 | return result; 1336 | } 1337 | 1338 | decimal round() const { 1339 | int64 resultPayload; 1340 | RoundPolicy::div_rounded(resultPayload, this->m_value, DecimalFactor::value); 1341 | decimal result(resultPayload); 1342 | return result; 1343 | } 1344 | 1345 | /// returns value rounded to integer using active rounding policy 1346 | int64 getAsInteger() const { 1347 | int64 result; 1348 | RoundPolicy::div_rounded(result, m_value, DecimalFactor::value); 1349 | return result; 1350 | } 1351 | 1352 | /// overwrites internal value with integer 1353 | void setAsInteger(int64 value) { 1354 | m_value = DecimalFactor::value * value; 1355 | } 1356 | 1357 | /// Returns two parts: before and after decimal point 1358 | /// For negative values both numbers are negative or zero. 1359 | void unpack(int64 &beforeValue, int64 &afterValue) const { 1360 | afterValue = m_value % DecimalFactor::value; 1361 | beforeValue = (m_value - afterValue) / DecimalFactor::value; 1362 | } 1363 | 1364 | /// Combines two parts (before and after decimal point) into decimal value. 1365 | /// Both input values have to have the same sign for correct results. 1366 | /// Does not perform any rounding or input validation - afterValue must be less than 10^prec. 1367 | /// \param[in] beforeValue value before decimal point 1368 | /// \param[in] afterValue value after decimal point multiplied by 10^prec 1369 | /// \result Returns *this 1370 | decimal &pack(int64 beforeValue, int64 afterValue) { 1371 | if (Prec > 0) { 1372 | m_value = beforeValue * DecimalFactor::value; 1373 | m_value += (afterValue % DecimalFactor::value); 1374 | } else 1375 | m_value = beforeValue * DecimalFactor::value; 1376 | return *this; 1377 | } 1378 | 1379 | /// Version of pack() with rounding, sourcePrec specifies precision of source values. 1380 | /// See also @pack. 1381 | template 1382 | decimal &pack_rounded(int64 beforeValue, int64 afterValue) { 1383 | decimal temp; 1384 | temp.pack(beforeValue, afterValue); 1385 | decimal result(temp.getUnbiased(), temp.getPrecFactor()); 1386 | 1387 | *this = result; 1388 | return *this; 1389 | } 1390 | 1391 | static decimal buildWithExponent(int64 mantissa, int exponent) { 1392 | decimal result; 1393 | result.setWithExponent(mantissa, exponent); 1394 | return result; 1395 | } 1396 | 1397 | static decimal &buildWithExponent(decimal &output, int64 mantissa, 1398 | int exponent) { 1399 | output.setWithExponent(mantissa, exponent); 1400 | return output; 1401 | } 1402 | 1403 | void setWithExponent(int64 mantissa, int exponent) { 1404 | 1405 | int exponentForPack = exponent + Prec; 1406 | 1407 | if (exponentForPack < 0) { 1408 | int64 newValue; 1409 | 1410 | if (!RoundPolicy::div_rounded(newValue, mantissa, 1411 | dec_utils::pow10(-exponentForPack))) { 1412 | newValue = 0; 1413 | } 1414 | 1415 | m_value = newValue; 1416 | } else { 1417 | m_value = mantissa * dec_utils::pow10(exponentForPack); 1418 | } 1419 | } 1420 | 1421 | void getWithExponent(int64 &mantissa, int &exponent) const { 1422 | int64 value = m_value; 1423 | int exp = -Prec; 1424 | 1425 | if (value != 0) { 1426 | // normalize 1427 | while (value % 10 == 0) { 1428 | value /= 10; 1429 | exp++; 1430 | } 1431 | } 1432 | 1433 | mantissa = value; 1434 | exponent = exp; 1435 | } 1436 | 1437 | protected: 1438 | inline xdouble getPrecFactorXDouble() const { 1439 | return static_cast(DecimalFactor::value); 1440 | } 1441 | 1442 | inline double getPrecFactorDouble() const { 1443 | return static_cast(DecimalFactor::value); 1444 | } 1445 | 1446 | void init(const decimal &src) { 1447 | m_value = src.m_value; 1448 | } 1449 | 1450 | void init(uint value) { 1451 | m_value = DecimalFactor::value * value; 1452 | } 1453 | 1454 | void init(int value) { 1455 | m_value = DecimalFactor::value * value; 1456 | } 1457 | 1458 | #ifdef DEC_HANDLE_LONG 1459 | void init(long int value) { 1460 | m_value = DecimalFactor::value * value; 1461 | } 1462 | #endif 1463 | 1464 | void init(int64 value) { 1465 | m_value = DecimalFactor::value * value; 1466 | } 1467 | 1468 | void init(xdouble value) { 1469 | m_value = fpToStorage(value); 1470 | } 1471 | 1472 | void init(double value) { 1473 | m_value = fpToStorage(value); 1474 | } 1475 | 1476 | void init(float value) { 1477 | m_value = fpToStorage(static_cast(value)); 1478 | } 1479 | 1480 | void initWithPrec(int64 value, int64 precFactor) { 1481 | int64 ownFactor = DecimalFactor::value; 1482 | 1483 | if (ownFactor == precFactor) { 1484 | // no conversion required 1485 | m_value = value; 1486 | } 1487 | else if (ownFactor > precFactor) { 1488 | m_value = value * (ownFactor / precFactor); 1489 | } 1490 | else { 1491 | // conversion 1492 | RoundPolicy::div_rounded(m_value, value, precFactor / ownFactor); 1493 | } 1494 | } 1495 | 1496 | template 1497 | static dec_storage_t fpToStorage(T value) { 1498 | dec_storage_t intPart = dec_utils::trunc(value); 1499 | T fracPart = value - intPart; 1500 | return RoundPolicy::round( 1501 | static_cast(DecimalFactor::value) * fracPart) + 1502 | DecimalFactor::value * intPart; 1503 | } 1504 | 1505 | template 1506 | static T abs(T value) { 1507 | if (value < 0) 1508 | return -value; 1509 | else 1510 | return value; 1511 | } 1512 | 1513 | static int sign(int64 value) { 1514 | return (value > 0) ? 1 : ((value < 0) ? -1 : 0); 1515 | } 1516 | protected: 1517 | dec_storage_t m_value; 1518 | }; 1519 | 1520 | // ---------------------------------------------------------------------------- 1521 | // Pre-defined types 1522 | // ---------------------------------------------------------------------------- 1523 | typedef decimal<2> decimal2; 1524 | typedef decimal<4> decimal4; 1525 | typedef decimal<6> decimal6; 1526 | 1527 | // ---------------------------------------------------------------------------- 1528 | // global functions 1529 | // ---------------------------------------------------------------------------- 1530 | template 1531 | decimal decimal_cast(const T &arg) { 1532 | return decimal(arg.getUnbiased(), arg.getPrecFactor()); 1533 | } 1534 | 1535 | // Example of use: 1536 | // c = dec::decimal_cast<6>(a * b); 1537 | template 1538 | decimal decimal_cast(uint arg) { 1539 | decimal result(arg); 1540 | return result; 1541 | } 1542 | 1543 | template 1544 | decimal decimal_cast(int arg) { 1545 | decimal result(arg); 1546 | return result; 1547 | } 1548 | 1549 | template 1550 | decimal decimal_cast(int64 arg) { 1551 | decimal result(arg); 1552 | return result; 1553 | } 1554 | 1555 | template 1556 | decimal decimal_cast(double arg) { 1557 | decimal result(arg); 1558 | return result; 1559 | } 1560 | 1561 | template 1562 | decimal decimal_cast(const std::string &arg) { 1563 | decimal result(arg); 1564 | return result; 1565 | } 1566 | 1567 | template 1568 | decimal decimal_cast(const char (&arg)[N]) { 1569 | decimal result(arg); 1570 | return result; 1571 | } 1572 | 1573 | // with rounding policy 1574 | template 1575 | decimal decimal_cast(uint arg) { 1576 | decimal result(arg); 1577 | return result; 1578 | } 1579 | 1580 | template 1581 | decimal decimal_cast(int arg) { 1582 | decimal result(arg); 1583 | return result; 1584 | } 1585 | 1586 | template 1587 | decimal decimal_cast(int64 arg) { 1588 | decimal result(arg); 1589 | return result; 1590 | } 1591 | 1592 | template 1593 | decimal decimal_cast(double arg) { 1594 | decimal result(arg); 1595 | return result; 1596 | } 1597 | 1598 | template 1599 | decimal decimal_cast(const std::string &arg) { 1600 | decimal result(arg); 1601 | return result; 1602 | } 1603 | 1604 | template 1605 | decimal decimal_cast(const char (&arg)[N]) { 1606 | decimal result(arg); 1607 | return result; 1608 | } 1609 | 1610 | // value format with constant default values 1611 | class basic_decimal_format { 1612 | public: 1613 | virtual bool change_thousands_if_needed() const { return true; } 1614 | 1615 | virtual char decimal_point() const { 1616 | return '.'; 1617 | } 1618 | 1619 | virtual char thousands_sep() const { 1620 | return ','; 1621 | } 1622 | 1623 | virtual bool thousands_grouping() const { 1624 | return false; 1625 | } 1626 | 1627 | virtual std::string grouping() const { 1628 | return ""; 1629 | } 1630 | }; 1631 | 1632 | // value format with full specification stored in fields 1633 | class decimal_format: public basic_decimal_format { 1634 | public: 1635 | 1636 | decimal_format(char decimal_point) : 1637 | m_decimal_point(decimal_point), 1638 | m_thousands_sep(','), 1639 | m_thousands_grouping(false), 1640 | m_grouping("") { 1641 | } 1642 | 1643 | decimal_format(char decimal_point, char thousands_sep) : m_decimal_point(decimal_point), 1644 | m_thousands_sep(thousands_sep), 1645 | m_thousands_grouping(thousands_sep != '\0'), 1646 | m_grouping(thousands_sep != '\0' ? "\03" : "") { 1647 | 1648 | } 1649 | 1650 | decimal_format(char decimal_point, char thousands_sep, bool thousands_grouping) : m_decimal_point(decimal_point), 1651 | m_thousands_sep(thousands_sep), 1652 | m_thousands_grouping(thousands_grouping), 1653 | m_grouping(thousands_grouping ? "\03" : "") { 1654 | 1655 | } 1656 | 1657 | decimal_format(char decimal_point, char thousands_sep, bool thousands_grouping, const std::string &grouping) : 1658 | m_decimal_point(decimal_point), 1659 | m_thousands_sep(thousands_sep), 1660 | m_thousands_grouping(thousands_grouping), 1661 | m_grouping(grouping) { 1662 | 1663 | } 1664 | 1665 | decimal_format(const decimal_format &source): m_decimal_point(source.m_decimal_point), 1666 | m_thousands_sep(source.m_thousands_sep), 1667 | m_thousands_grouping(source.m_thousands_grouping), 1668 | m_grouping(source.m_grouping) { 1669 | 1670 | } 1671 | 1672 | decimal_format & operator=(const decimal_format &rhs) { 1673 | if (&rhs != this) { 1674 | m_decimal_point = rhs.m_decimal_point; 1675 | m_thousands_sep = rhs.m_thousands_sep; 1676 | m_thousands_grouping = rhs.m_thousands_grouping; 1677 | m_grouping = rhs.grouping(); 1678 | } 1679 | return *this; 1680 | } 1681 | 1682 | char decimal_point() const DEC_OVERRIDE { 1683 | return m_decimal_point; 1684 | } 1685 | 1686 | char thousands_sep() const DEC_OVERRIDE { 1687 | return m_thousands_sep; 1688 | } 1689 | 1690 | bool thousands_grouping() const DEC_OVERRIDE { 1691 | return m_thousands_grouping; 1692 | } 1693 | 1694 | std::string grouping() const DEC_OVERRIDE { 1695 | return m_grouping; 1696 | } 1697 | 1698 | private: 1699 | char m_decimal_point; 1700 | char m_thousands_sep; 1701 | bool m_thousands_grouping; 1702 | std::string m_grouping; 1703 | }; 1704 | 1705 | class decimal_format_punct : public std::numpunct 1706 | { 1707 | public: 1708 | decimal_format_punct(const basic_decimal_format &format): m_format(format) {} 1709 | 1710 | protected: 1711 | virtual char do_thousands_sep() const { return m_format.thousands_sep(); } 1712 | virtual std::string do_grouping() const { return m_format.grouping(); } 1713 | const basic_decimal_format &m_format; 1714 | }; 1715 | 1716 | template 1717 | decimal_format format_from_stream(StreamType &stream) { 1718 | using namespace std; 1719 | const numpunct *facet = 1720 | has_facet >(stream.getloc()) ? 1721 | &use_facet >(stream.getloc()) : NULL; 1722 | const char dec_point = (facet != NULL) ? facet->decimal_point() : '.'; 1723 | const bool thousands_grouping = 1724 | (facet != NULL) ? (!facet->grouping().empty()) : false; 1725 | const char thousands_sep = (facet != NULL) ? facet->thousands_sep() : ','; 1726 | string grouping_spec = 1727 | (facet != NULL) ? (facet->grouping()) : string(""); 1728 | return decimal_format(dec_point, thousands_sep, thousands_grouping, grouping_spec); 1729 | } 1730 | 1731 | /// Exports decimal to stream 1732 | /// Used format: {-}bbbb.aaaa where 1733 | /// {-} is optional '-' sign character 1734 | /// '.' is locale-dependent decimal point character 1735 | /// bbbb is stream of digits before decimal point 1736 | /// aaaa is stream of digits after decimal point 1737 | template 1738 | void toStream(const decimal_type &arg, const basic_decimal_format &format, StreamType &output, bool formatFromStream = false) { 1739 | using namespace std; 1740 | 1741 | int64 before, after; 1742 | int sign; 1743 | 1744 | arg.unpack(before, after); 1745 | sign = 1; 1746 | 1747 | if (before < 0) { 1748 | sign = -1; 1749 | before = -before; 1750 | } 1751 | 1752 | if (after < 0) { 1753 | sign = -1; 1754 | after = -after; 1755 | } 1756 | 1757 | if (sign < 0) { 1758 | output << "-"; 1759 | } 1760 | 1761 | std::locale oldloc = output.getloc(); 1762 | if (!formatFromStream || (format.thousands_grouping() && format.change_thousands_if_needed())) { 1763 | output.imbue( std::locale( std::locale::classic(), new decimal_format_punct(format) ) ); 1764 | output << before; 1765 | output.imbue(oldloc); 1766 | } else { 1767 | output << before; 1768 | } 1769 | 1770 | if (arg.getDecimalPoints() > 0) { 1771 | output.imbue(std::locale::classic()); 1772 | output << format.decimal_point(); 1773 | output << setw(arg.getDecimalPoints()) << setfill('0') << right 1774 | << after; 1775 | output.imbue(oldloc); 1776 | } 1777 | } 1778 | 1779 | template 1780 | void toStream(const decimal_type &arg, StreamType &output) { 1781 | toStream(arg, format_from_stream(output), output, true); 1782 | } 1783 | 1784 | namespace details { 1785 | 1786 | /// Extract values from stream ready to be packed to decimal 1787 | template 1788 | bool parse_unpacked(StreamType &input, const basic_decimal_format &format, int &sign, int64 &before, int64 &after, 1789 | int &decimalDigits) { 1790 | 1791 | const char dec_point = format.decimal_point(); 1792 | const bool thousands_grouping = format.thousands_grouping(); 1793 | const char thousands_sep = format.thousands_sep(); 1794 | 1795 | enum StateEnum { 1796 | IN_SIGN, IN_BEFORE_FIRST_DIG, IN_BEFORE_DEC, IN_AFTER_DEC, IN_END 1797 | } state = IN_SIGN; 1798 | enum ErrorCodes { 1799 | ERR_WRONG_CHAR = -1, 1800 | ERR_NO_DIGITS = -2, 1801 | ERR_WRONG_STATE = -3, 1802 | ERR_STREAM_GET_ERROR = -4 1803 | }; 1804 | 1805 | before = after = 0; 1806 | sign = 1; 1807 | 1808 | int error = 0; 1809 | int digitsCount = 0; 1810 | int afterDigitCount = 0; 1811 | char c; 1812 | 1813 | while ((input) && (state != IN_END)) // loop while extraction from file is possible 1814 | { 1815 | c = static_cast(input.get()); 1816 | 1817 | switch (state) { 1818 | case IN_SIGN: 1819 | if (c == '-') { 1820 | sign = -1; 1821 | state = IN_BEFORE_FIRST_DIG; 1822 | } else if (c == '+') { 1823 | state = IN_BEFORE_FIRST_DIG; 1824 | } else if ((c >= '0') && (c <= '9')) { 1825 | state = IN_BEFORE_DEC; 1826 | before = static_cast(c - '0'); 1827 | digitsCount++; 1828 | } else if (c == dec_point) { 1829 | state = IN_AFTER_DEC; 1830 | } else if ((c != ' ') && (c != '\t')) { 1831 | state = IN_END; 1832 | error = ERR_WRONG_CHAR; 1833 | } 1834 | // else ignore char 1835 | break; 1836 | case IN_BEFORE_FIRST_DIG: 1837 | if ((c >= '0') && (c <= '9')) { 1838 | before = 10 * before + static_cast(c - '0'); 1839 | state = IN_BEFORE_DEC; 1840 | digitsCount++; 1841 | } else if (c == dec_point) { 1842 | state = IN_AFTER_DEC; 1843 | } else { 1844 | state = IN_END; 1845 | error = ERR_WRONG_CHAR; 1846 | } 1847 | break; 1848 | case IN_BEFORE_DEC: 1849 | if ((c >= '0') && (c <= '9')) { 1850 | before = 10 * before + static_cast(c - '0'); 1851 | digitsCount++; 1852 | } else if (c == dec_point) { 1853 | state = IN_AFTER_DEC; 1854 | } else if (thousands_grouping && c == thousands_sep) { 1855 | ; // ignore the char 1856 | } else { 1857 | state = IN_END; 1858 | } 1859 | break; 1860 | case IN_AFTER_DEC: 1861 | if ((c >= '0') && (c <= '9')) { 1862 | after = 10 * after + static_cast(c - '0'); 1863 | afterDigitCount++; 1864 | if (afterDigitCount >= DEC_NAMESPACE::max_decimal_points) 1865 | state = IN_END; 1866 | } else { 1867 | state = IN_END; 1868 | if (digitsCount == 0) { 1869 | error = ERR_NO_DIGITS; 1870 | } 1871 | } 1872 | break; 1873 | default: 1874 | error = ERR_WRONG_STATE; 1875 | state = IN_END; 1876 | break; 1877 | } // switch state 1878 | } // while stream good & not end 1879 | 1880 | decimalDigits = afterDigitCount; 1881 | 1882 | if (error >= 0) { 1883 | 1884 | if (sign < 0) { 1885 | before = -before; 1886 | after = -after; 1887 | } 1888 | 1889 | } else { 1890 | before = after = 0; 1891 | } 1892 | 1893 | return (error >= 0); 1894 | } // function 1895 | 1896 | template 1897 | bool parse_unpacked(StreamType &input, int &sign, int64 &before, int64 &after, 1898 | int &decimalDigits) { 1899 | return parse_unpacked(input, format_from_stream(input), sign, before, after, decimalDigits); 1900 | } 1901 | } 1902 | ; 1903 | // namespace 1904 | 1905 | /// Converts stream of chars to decimal 1906 | /// Handles the following formats ('.' is selected from locale info): 1907 | /// \code 1908 | /// 123 1909 | /// -123 1910 | /// 123.0 1911 | /// -123.0 1912 | /// 123. 1913 | /// .123 1914 | /// 0. 1915 | /// -.123 1916 | /// \endcode 1917 | /// Spaces and tabs on the front are ignored. 1918 | /// Performs rounding when provided value has higher precision than in output type. 1919 | /// \param[in] input input stream 1920 | /// \param[out] output decimal value, 0 on error 1921 | /// \result Returns true if conversion succeeded 1922 | template 1923 | bool fromStream(StreamType &input, const basic_decimal_format &format, decimal_type &output) { 1924 | int sign, afterDigits; 1925 | int64 before, after; 1926 | bool result = details::parse_unpacked(input, format, sign, before, after, 1927 | afterDigits); 1928 | if (result) { 1929 | if (afterDigits <= decimal_type::decimal_points) { 1930 | // direct mode 1931 | int corrCnt = decimal_type::decimal_points - afterDigits; 1932 | while (corrCnt > 0) { 1933 | after *= 10; 1934 | --corrCnt; 1935 | } 1936 | output.pack(before, after); 1937 | } else { 1938 | // rounding mode 1939 | int corrCnt = afterDigits; 1940 | int64 decimalFactor = 1; 1941 | while (corrCnt > 0) { 1942 | before *= 10; 1943 | decimalFactor *= 10; 1944 | --corrCnt; 1945 | } 1946 | decimal_type temp(before + after, decimalFactor); 1947 | output = temp; 1948 | } 1949 | } else { 1950 | output = decimal_type(0); 1951 | } 1952 | return result; 1953 | } 1954 | 1955 | template 1956 | bool fromStream(StreamType &input, decimal_type &output) { 1957 | return fromStream(input, format_from_stream(input), output); 1958 | } 1959 | 1960 | /// Exports decimal to string 1961 | /// Used format: {-}bbbb.aaaa where 1962 | /// {-} is optional '-' sign character 1963 | /// '.' is locale-dependent decimal point character 1964 | /// bbbb is stream of digits before decimal point 1965 | /// aaaa is stream of digits after decimal point 1966 | template 1967 | std::string &toString(const decimal &arg, 1968 | const basic_decimal_format &format, 1969 | std::string &output) { 1970 | using namespace std; 1971 | 1972 | ostringstream out; 1973 | toStream(arg, format, out); 1974 | output = DEC_MOVE(out.str()); 1975 | return output; 1976 | } 1977 | 1978 | template 1979 | std::string &toString(const decimal &arg, 1980 | std::string &output) { 1981 | using namespace std; 1982 | 1983 | ostringstream out; 1984 | toStream(arg, out); 1985 | output = DEC_MOVE(out.str()); 1986 | return output; 1987 | } 1988 | 1989 | /// Exports decimal to string 1990 | /// Used format: {-}bbbb.aaaa where 1991 | /// {-} is optional '-' sign character 1992 | /// '.' is locale-dependent decimal point character 1993 | /// bbbb is stream of digits before decimal point 1994 | /// aaaa is stream of digits after decimal point 1995 | template 1996 | std::string toString(const decimal &arg) { 1997 | std::string res; 1998 | toString(arg, res); 1999 | return res; 2000 | } 2001 | 2002 | template 2003 | std::string toString(const decimal &arg, const basic_decimal_format &format) { 2004 | std::string res; 2005 | toString(arg, format, res); 2006 | return res; 2007 | } 2008 | 2009 | // input 2010 | template 2011 | std::basic_istream & 2012 | operator>>(std::basic_istream & is, 2013 | decimal & d) { 2014 | if (!fromStream(is, d)) 2015 | d.setUnbiased(0); 2016 | return is; 2017 | } 2018 | 2019 | // output 2020 | template 2021 | std::basic_ostream & 2022 | operator<<(std::basic_ostream & os, 2023 | const decimal & d) { 2024 | toStream(d, os); 2025 | return os; 2026 | } 2027 | 2028 | /// Imports decimal from string 2029 | /// Used format: {-}bbbb.aaaa where 2030 | /// {-} is optional '-' sign character 2031 | /// '.' is locale-dependent decimal point character 2032 | /// bbbb is stream of digits before decimal point 2033 | /// aaaa is stream of digits after decimal point 2034 | template 2035 | T fromString(const std::string &str) { 2036 | std::istringstream is(str); 2037 | T t; 2038 | 2039 | if (!fromStream(is, t)) { 2040 | t.setUnbiased(0); 2041 | } 2042 | 2043 | return t; 2044 | } 2045 | 2046 | template 2047 | T fromString(const std::string &str, const basic_decimal_format &format) { 2048 | std::istringstream is(str); 2049 | T t; 2050 | 2051 | if (!fromStream(is, format, t)) { 2052 | t.setUnbiased(0); 2053 | } 2054 | 2055 | return t; 2056 | } 2057 | 2058 | template 2059 | void fromString(const std::string &str, const basic_decimal_format &format, T &out) { 2060 | out = fromString(str, format); 2061 | } 2062 | 2063 | template 2064 | void fromString(const std::string &str, T &out) { 2065 | out = fromString(str); 2066 | } 2067 | 2068 | } // namespace 2069 | #endif // _DECIMAL_H__ 2070 | -------------------------------------------------------------------------------- /tests/decimalTest.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | // Name: decimalTest.cpp 3 | // Purpose: Test decimal type. 4 | // Author: Piotr Likus 5 | // Modified by: 6 | // Created: 31/10/2010 7 | // Licence: BSD 8 | ///////////////////////////////////////////////////////////////////////////// 9 | 10 | #include "decimal.h" 11 | 12 | 13 | #include 14 | #include 15 | 16 | #ifndef DEC_NO_CPP11 17 | #include 18 | #endif 19 | 20 | BOOST_AUTO_TEST_CASE(decimalAsInteger) 21 | { 22 | // rounded value 23 | dec::decimal<6> a; 24 | a = dec::decimal<6>("2305843009213.693952"); 25 | dec::DEC_INT64 expectedValue = 2305843009214; 26 | BOOST_CHECK_EQUAL(a.getAsInteger(), expectedValue); 27 | 28 | // int64 value (>0) 29 | expectedValue = 23058430092136939; 30 | dec::decimal<1> b = dec::decimal<1>(expectedValue); 31 | BOOST_CHECK_EQUAL(b.getAsInteger(), expectedValue); 32 | 33 | // int64 value (<0) 34 | expectedValue = -23058430092136939; 35 | dec::decimal<1> c = dec::decimal<1>(expectedValue); 36 | BOOST_CHECK_EQUAL(c.getAsInteger(), expectedValue); 37 | } 38 | 39 | BOOST_AUTO_TEST_CASE(issue67) 40 | { 41 | using namespace std; 42 | dec::decimal<4, dec::half_even_round_policy> p1("350.68"); 43 | dec::decimal<4, dec::half_even_round_policy> p2("359.2050"); 44 | 45 | dec::decimal<0, dec::half_even_round_policy> s1("550"); 46 | dec::decimal<0, dec::half_even_round_policy> s2("550"); 47 | 48 | dec::decimal<0, dec::half_even_round_policy> s_sum = s1 + s2; 49 | dec::decimal<4, dec::half_even_round_policy> p_sum = p1 + p2; 50 | dec::decimal<4, dec::half_even_round_policy> result = (p_sum / s_sum); 51 | 52 | dec::decimal<4, dec::half_even_round_policy> expected("0.6454"); 53 | 54 | BOOST_CHECK_EQUAL(expected, result); 55 | } 56 | 57 | // test with values internally > 2^32 58 | BOOST_AUTO_TEST_CASE(decimalMidOverflow) 59 | { 60 | dec::decimal<6> a; 61 | dec::decimal<6> b; 62 | dec::decimal<6> c; 63 | dec::decimal<6> expected; 64 | 65 | a = dec::decimal<6>("2305843009213.693952"); // 2^61 66 | b = dec::decimal<6>("2.000001"); // value < 2^32 67 | 68 | c = a * b; 69 | expected = dec::decimal<6>("4611688324270.397118"); 70 | 71 | // overflow, so calculation via floating point, error will be <> 0, but should be small 72 | BOOST_CHECK((c - expected).abs() < dec::decimal<6>("1.0")); 73 | } 74 | 75 | BOOST_AUTO_TEST_CASE(decimalUnpack) 76 | { 77 | using namespace dec; 78 | 79 | decimal<4> d(-0.5); 80 | int64 before, after; 81 | d.unpack(before, after); 82 | BOOST_CHECK(before == 0); 83 | BOOST_CHECK(after == -5000); 84 | 85 | decimal<4> d1(-1.5); 86 | d1.unpack(before, after); 87 | BOOST_CHECK(before == -1); 88 | BOOST_CHECK(after == -5000); 89 | 90 | decimal<4> d2(1.5121); 91 | d2.unpack(before, after); 92 | BOOST_CHECK(before == 1); 93 | BOOST_CHECK(after == 5121); 94 | 95 | decimal<4> d3(0.5121); 96 | d3.unpack(before, after); 97 | BOOST_CHECK(before == 0); 98 | BOOST_CHECK(after == 5121); 99 | } 100 | 101 | BOOST_AUTO_TEST_CASE(decimalPack) 102 | { 103 | using namespace dec; 104 | decimal<4> d; 105 | BOOST_CHECK(decimal<4>().pack(2, 5121) == decimal<4>(2.5121)); 106 | BOOST_CHECK(d.pack(1, 1) == decimal<4>("1.0001")); 107 | BOOST_CHECK(d.pack(1, 1000) == decimal<4>("1.1")); 108 | BOOST_CHECK(d.pack(0, 5121) == decimal<4>(0.5121)); 109 | BOOST_CHECK(d.pack(0, -5121) == decimal<4>(-0.5121)); 110 | BOOST_CHECK(d.pack(1, 5121) == decimal<4>(1.5121)); 111 | BOOST_CHECK(d.pack(-1, 0) == decimal<4>(-1.0)); 112 | BOOST_CHECK(d.pack(-1, -5121) == decimal<4>(-1.5121)); 113 | BOOST_CHECK(d.pack(-1, 5121) != decimal<4>(-1.5121)); 114 | BOOST_CHECK(d.pack(1, -5121) != decimal<4>(-1.5121)); 115 | // trimming (no rounding) for default pack 116 | BOOST_CHECK(d.pack(1, 51210) != decimal<4>("1.5121")); 117 | BOOST_CHECK(d.pack(1, 51215) != decimal<4>("1.5121")); 118 | // pack with rounding 119 | BOOST_CHECK(decimal_cast<4>(decimal<5>().pack(1, 51215)) == decimal<4>("1.5122")); 120 | BOOST_CHECK(d.pack_rounded<5>(1, 51216) == decimal<4>("1.5122")); 121 | BOOST_CHECK(d.pack_rounded<5>(1, 51215) == decimal<4>("1.5122")); 122 | BOOST_CHECK(d.pack_rounded<5>(1, 51214) == decimal<4>("1.5121")); 123 | BOOST_CHECK(d.pack_rounded<5>(1, 51211) == decimal<4>("1.5121")); 124 | } 125 | 126 | BOOST_AUTO_TEST_CASE(decimalZeroPrec) 127 | { 128 | using namespace dec; 129 | decimal<0> d; 130 | 131 | // integer values 132 | BOOST_CHECK(decimal<0>().pack(25121, 0) == decimal<0>(25121)); 133 | BOOST_CHECK(d.pack(5121, 0) == decimal<0>(5121)); 134 | 135 | // trimming (no rounding in pack) 136 | BOOST_CHECK(decimal<0>().pack(2, 5121) == decimal<0>(2)); 137 | BOOST_CHECK(d.pack(0, 5121) == decimal<0>(0)); 138 | BOOST_CHECK(d.pack(0, -5121) == decimal<0>(0)); 139 | BOOST_CHECK(d.pack(1, 5121) == decimal<0>(1)); 140 | BOOST_CHECK(d.pack(-1, -5121) == decimal<0>(-1)); 141 | BOOST_CHECK(d.pack(-1, 5121) == decimal<0>(-1)); 142 | 143 | // for rounding use decimal_cast from source prec 144 | d = decimal_cast<0>(decimal<4>().pack(-1, -5121)); 145 | BOOST_CHECK(d == decimal<0>(-2)); 146 | } 147 | 148 | BOOST_AUTO_TEST_CASE(decimalComparison) 149 | { 150 | const double pi = 3.1415926; 151 | BOOST_CHECK_EQUAL(dec::decimal<8>(0.), 0); 152 | BOOST_CHECK_EQUAL(dec::decimal<8>(0.), 0u); 153 | #ifdef DEC_HANDLE_LONG 154 | BOOST_CHECK_EQUAL(dec::decimal<8>(0.), 0L); 155 | #endif 156 | BOOST_CHECK_EQUAL(dec::decimal<8>(0.), 0.); 157 | BOOST_CHECK_EQUAL(dec::decimal<8>(pi), pi); 158 | BOOST_CHECK_EQUAL(dec::decimal<8>(pi), dec::decimal<8>(pi)); 159 | BOOST_CHECK(dec::decimal<8>(pi) < 5); 160 | BOOST_CHECK(dec::decimal<8>(pi) <= 5); 161 | BOOST_CHECK(dec::decimal<8>(pi) <= pi); 162 | BOOST_CHECK(dec::decimal<8>(pi) > 3); 163 | BOOST_CHECK(dec::decimal<8>(pi) >= 3); 164 | BOOST_CHECK(dec::decimal<8>(pi) >= pi); 165 | BOOST_CHECK(dec::decimal<8>(pi) != 3); 166 | BOOST_CHECK(dec::decimal<8>(pi) != 3.0); 167 | } 168 | 169 | BOOST_AUTO_TEST_CASE(decimalSign) 170 | { 171 | dec::decimal<4> d(-4.1234); 172 | BOOST_CHECK(d.sign() < 0); 173 | 174 | d *= -1; 175 | BOOST_CHECK(d.sign() > 0); 176 | 177 | d = -d; 178 | BOOST_CHECK(d.sign() < 0); 179 | 180 | d = d.abs(); 181 | BOOST_CHECK(d.sign() > 0); 182 | 183 | d -= d; 184 | BOOST_CHECK(d.sign() == 0); 185 | } 186 | 187 | BOOST_AUTO_TEST_CASE(decimalFloatConstructorHighPrec) { 188 | dec::decimal<2> d1(3.1549999999999998); 189 | dec::decimal<2> d2("3.1549999999999998"); 190 | BOOST_CHECK_EQUAL(d1, d2); 191 | 192 | dec::decimal<2> d3(-3.1549999999999998); 193 | dec::decimal<2> d4("-3.1549999999999998"); 194 | BOOST_CHECK_EQUAL(d3, d4); 195 | 196 | dec::decimal<2> d5; 197 | d5.setAsDouble(3.1549999999999998); 198 | BOOST_CHECK_EQUAL(d5, d2); 199 | 200 | dec::decimal<2> d6; 201 | d6 = 3.1549999999999998; 202 | BOOST_CHECK_EQUAL(d6, d2); 203 | } 204 | 205 | #ifndef DEC_NO_CPP11 206 | BOOST_AUTO_TEST_CASE(trivialAndNoThrowConstructor) { 207 | #ifdef DEC_TRIVIAL_DEFAULT_CONSTRUCTIBLE 208 | BOOST_CHECK_EQUAL(std::is_trivial>::value, true); 209 | #else 210 | BOOST_CHECK_EQUAL(std::is_trivial>::value, false); 211 | #endif 212 | 213 | #ifdef DEC_TRIVIAL_DEFAULT_CONSTRUCTIBLE 214 | BOOST_CHECK_EQUAL(std::is_trivially_constructible>::value, true); 215 | #else 216 | BOOST_CHECK_EQUAL(std::is_trivially_constructible>::value, false); 217 | #endif 218 | BOOST_CHECK_EQUAL(std::is_nothrow_constructible>::value, true); 219 | 220 | #ifdef DEC_TRIVIAL_DEFAULT_CONSTRUCTIBLE 221 | BOOST_CHECK_EQUAL(std::is_trivially_default_constructible>::value, true); 222 | #else 223 | BOOST_CHECK_EQUAL(std::is_trivially_default_constructible>::value, false); 224 | #endif 225 | BOOST_CHECK_EQUAL(std::is_nothrow_default_constructible>::value, true); 226 | 227 | BOOST_CHECK_EQUAL(std::is_trivially_copy_constructible>::value, true); 228 | BOOST_CHECK_EQUAL(std::is_nothrow_copy_constructible>::value, true); 229 | 230 | BOOST_CHECK_EQUAL(std::is_trivially_copy_assignable>::value, true); 231 | BOOST_CHECK_EQUAL(std::is_nothrow_copy_assignable>::value, true); 232 | 233 | BOOST_CHECK_EQUAL(std::is_trivially_destructible>::value, true); 234 | BOOST_CHECK_EQUAL(std::is_nothrow_destructible>::value, true); 235 | } 236 | #endif 237 | 238 | -------------------------------------------------------------------------------- /tests/decimalTestAbout.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Demo of features mentioned in project README 3 | // Created by piotr on 7/19/21. 4 | // 5 | 6 | #include "decimal.h" 7 | #include 8 | 9 | BOOST_AUTO_TEST_CASE(decimalAbout) { 10 | using namespace dec; 11 | using namespace std; 12 | 13 | // the following declares currency variable with 2 decimal points 14 | // initialized with integer value (can be also floating-point) 15 | decimal<2> value(143125); 16 | 17 | // displays: Value #1 is: 143125.00 18 | cout << "Value #1 is: " << value << endl; 19 | 20 | // declare precise value with digits after decimal point 21 | decimal<2> b("0.11"); 22 | 23 | // perform calculations as with any other numeric type 24 | value += b; 25 | 26 | // displays: Value #2 is: 143125.11 27 | cout << "Value #2 is: " << value << endl; 28 | 29 | // automatic rounding performed here 30 | value /= 1000; 31 | 32 | // displays: Value #3 is: 143.13 33 | cout << "Value #3 is: " << value << endl; 34 | 35 | // integer multiplication and division can be used directly in expression 36 | // when integer is on right side 37 | // displays: Value: 143.13 * 2 is: 286.26 38 | cout << "Value: " << value << " * 2 is: " << (value * 2) << endl; 39 | 40 | // to use integer on left side you need to cast it 41 | // displays: Value: 2 * 143.13 is: 286.26 42 | cout << "Value: 2 * " << value << " is: " << (decimal_cast<2>(2) * value) << endl; 43 | 44 | // to use non-integer constants in expressions you need to use decimal_cast 45 | value = value * decimal_cast<2>("3.33") / decimal_cast<2>(333.0); 46 | 47 | // displays: Value #4 is: 1.43 48 | cout << "Value #4 is: " << value << endl; 49 | 50 | // to mix decimals with different precision use decimal_cast 51 | // it will round result automatically 52 | decimal<6> exchangeRate(12.1234); 53 | value = decimal_cast<2>(decimal_cast<6>(value) * exchangeRate); 54 | 55 | // displays: Value #5 is: 17.34 56 | cout << "Value #5 is: " << value << endl; 57 | 58 | // with doubles you would have to perform rounding each time it is required: 59 | double dvalue = 143125.0; 60 | dvalue += 0.11; 61 | dvalue /= 1000.0; 62 | dvalue = round(dvalue * 100.0) / 100.0; 63 | dvalue = (dvalue * 3.33) / 333.0; 64 | dvalue = round(dvalue * 100.0) / 100.0; 65 | dvalue = dvalue * 12.1234; 66 | dvalue = round(dvalue * 100.0) / 100.0; 67 | 68 | // displays: Value #5 calculated with double is: 17.34 69 | cout << "Value #5 calculated with double is: " << fixed << setprecision(2) << dvalue << endl; 70 | 71 | // supports optional strong typing, e.g. 72 | // depending on configuration mixing precision can be forbidden 73 | // or handled automatically 74 | decimal<2> d2("12.03"); 75 | decimal<4> d4("123.0103"); 76 | 77 | // compiles always 78 | d2 += d2; 79 | d2 += decimal_cast<2>(d4); 80 | d4 += decimal_cast<4>(d2); 81 | 82 | #if DEC_TYPE_LEVEL >= 2 83 | // potential precision loss 84 | // this will fail to compile if you define DEC_TYPE_LEVEL = 0 or 1 85 | d2 += d4; 86 | #endif 87 | 88 | #if DEC_TYPE_LEVEL >= 1 89 | // (possibly unintentional) mixed precision without type casting 90 | // this will fail to compile if you define DEC_TYPE_LEVEL = 0 91 | d4 += d2; 92 | #endif 93 | 94 | // for default setup displays: mixed d2 = 417.15 95 | cout << "mixed d2 = " << d2 << endl; 96 | // for default setup displays: mixed d4 = 687.2303 97 | cout << "mixed d4 = " << d4 << endl; 98 | } 99 | 100 | -------------------------------------------------------------------------------- /tests/decimalTestArithmetic.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 7/19/21. 3 | // 4 | 5 | #include "decimal.h" 6 | #include 7 | 8 | BOOST_AUTO_TEST_CASE(decimalArithmetic) 9 | { 10 | dec::decimal<2> balance; 11 | dec::decimal<2> a(123); 12 | dec::decimal<2> t(246); 13 | 14 | balance = 0; 15 | BOOST_TEST_MESSAGE("balance-0: " << toString(balance)); 16 | BOOST_CHECK(balance == dec::decimal2(0)); 17 | 18 | balance += a; 19 | BOOST_TEST_MESSAGE("balance-1: " << toString(balance)); 20 | BOOST_CHECK(balance != t); 21 | 22 | balance += a; 23 | BOOST_TEST_MESSAGE("balance-2: " << toString(balance)); 24 | BOOST_CHECK(balance == t); 25 | 26 | balance /= dec::decimal2(2); 27 | BOOST_TEST_MESSAGE("balance-3: " << toString(balance)); 28 | BOOST_CHECK(balance == a); 29 | 30 | balance *= dec::decimal2(2); 31 | BOOST_TEST_MESSAGE("balance-4: " << toString(balance)); 32 | BOOST_CHECK(balance == t); 33 | 34 | balance = balance / dec::decimal2(3); 35 | BOOST_TEST_MESSAGE("balance-5: " << toString(balance)); 36 | BOOST_CHECK(balance == dec::decimal2(82)); 37 | 38 | balance = balance + dec::decimal2(3); 39 | BOOST_TEST_MESSAGE("balance-6: " << toString(balance)); 40 | BOOST_CHECK(balance == dec::decimal2(85)); 41 | 42 | balance = balance - dec::decimal2(90); 43 | BOOST_TEST_MESSAGE("balance-7: " << toString(balance)); 44 | BOOST_CHECK(balance == dec::decimal2(-5)); 45 | 46 | balance = balance * dec::decimal2(9); 47 | BOOST_TEST_MESSAGE("balance-8: " << toString(balance)); 48 | BOOST_CHECK(balance == dec::decimal2(-45)); 49 | BOOST_CHECK(balance == dec::decimal2(-45.0)); 50 | 51 | // check div 52 | balance = balance / dec::decimal2(10.0); 53 | BOOST_TEST_MESSAGE("balance-9: " << toString(balance)); 54 | BOOST_CHECK(balance == dec::decimal2(-4.5)); 55 | 56 | balance *= dec::decimal2(3.0); 57 | BOOST_CHECK(balance == dec::decimal2(-13.5)); 58 | 59 | BOOST_CHECK(sizeof(dec::decimal2::raw_data_t) > 0); 60 | 61 | balance /= 10; 62 | BOOST_CHECK(balance == dec::decimal2(-1.35)); 63 | 64 | balance -= dec::decimal2(0.1); 65 | BOOST_CHECK(balance == dec::decimal2(-1.45)); 66 | 67 | balance = balance - dec::decimal2(3); 68 | BOOST_CHECK(balance == dec::decimal2(-4.45)); 69 | 70 | BOOST_CHECK(balance.abs() == dec::decimal2(4.45)); 71 | 72 | BOOST_CHECK(balance.getAsInteger() == -4); 73 | 74 | balance = +balance; 75 | BOOST_CHECK(balance == dec::decimal2(-4.45)); 76 | 77 | balance = -balance; 78 | BOOST_CHECK(balance == dec::decimal2(4.45)); 79 | 80 | BOOST_TEST_MESSAGE("balance-end: " << toString(balance)); 81 | } 82 | 83 | -------------------------------------------------------------------------------- /tests/decimalTestDiv.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 7/19/21. 3 | // 4 | 5 | #include "decimal.h" 6 | #include 7 | 8 | BOOST_AUTO_TEST_CASE(decimalDivInt) 9 | { 10 | BOOST_CHECK_EQUAL(dec::decimal<4>("1.0001") / 2, dec::decimal<4>("0.5001")); 11 | #ifdef DEC_HANDLE_LONG 12 | BOOST_CHECK_EQUAL(dec::decimal<4>("2.0010") / 2L, dec::decimal<4>("1.0005")); 13 | #endif 14 | } 15 | 16 | -------------------------------------------------------------------------------- /tests/decimalTestEdgeCases.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 7/19/21. 3 | // 4 | 5 | #include "decimal.h" 6 | #include "decimalTestUtils.h" 7 | #include 8 | 9 | #include 10 | 11 | #ifndef DEC_NO_CPP11 12 | #include 13 | #endif 14 | 15 | 16 | BOOST_AUTO_TEST_CASE(decimalHighEndVals) 17 | { 18 | // with decimal<16> we need to provide constants with strings not floating points. 19 | dec::decimal < 16 > a; 20 | dec::decimal<16> b; 21 | dec::decimal<16> c; 22 | 23 | a = dec::decimal<16>("1.1111111111111111"); 24 | b = dec::decimal<16>(2.0); 25 | 26 | c = a + b; 27 | BOOST_CHECK(c == dec::decimal<16>("3.1111111111111111")); 28 | 29 | c = a - b; 30 | BOOST_CHECK(c == dec::decimal<16>("-0.8888888888888889")); 31 | 32 | c = a * b; 33 | BOOST_CHECK(c == dec::decimal<16>("2.2222222222222222")); 34 | 35 | c = a / b; 36 | BOOST_CHECK(c == dec::decimal<16>("0.5555555555555556")); 37 | } 38 | 39 | BOOST_AUTO_TEST_CASE(decimalUHighEndVals) 40 | { 41 | dec::decimal < 17 > a; 42 | dec::decimal<17> b; 43 | dec::decimal<17> c; 44 | 45 | a = dec::decimal<17>("1.1"); 46 | b = dec::decimal<17>("2.0"); 47 | 48 | c = a + b; 49 | BOOST_CHECK(c == dec::decimal<17>("3.1")); 50 | 51 | c = a - b; 52 | BOOST_CHECK(c == dec::decimal<17>("-0.9")); 53 | 54 | c = a * b; 55 | BOOST_CHECK(c == dec::decimal<17>("2.2")); 56 | 57 | c = a / b; 58 | BOOST_CHECK(c == dec::decimal<17>("0.55")); 59 | } 60 | 61 | BOOST_AUTO_TEST_CASE(decimalMaxUIntCtor) 62 | { 63 | unsigned int uint_max = boost::integer_traits::const_max; 64 | std::string uint_max_txt = uint_to_string(uint_max); 65 | dec::decimal<2> a(uint_max); 66 | BOOST_CHECK_EQUAL(a, dec::decimal<2>(uint_max_txt)); 67 | BOOST_CHECK_EQUAL(a.getAsInteger(), uint_max); 68 | } 69 | 70 | BOOST_AUTO_TEST_CASE(decimalMaxInt64Ctor) 71 | { 72 | const dec::decimal<7> n(922337203685.4); 73 | BOOST_CHECK(n.getAsInteger() == 922337203685); 74 | } 75 | 76 | -------------------------------------------------------------------------------- /tests/decimalTestModulo.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 7/19/21. 3 | // 4 | 5 | #include "decimal.h" 6 | #include 7 | 8 | BOOST_AUTO_TEST_CASE(testModOperInt) { 9 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(15) % 4, 3); 10 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-15) % 4, -3); 11 | 12 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(11.9) % 4, 3.9); 13 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(12.1) % 4, 0.1); 14 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(12.5) % 4, 0.5); 15 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(12.9) % 4, 0.9); 16 | 17 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-11.9) % 4, -3.9); 18 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-12.1) % 4, -0.1); 19 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-12.5) % 4, -0.5); 20 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-12.9) % 4, -0.9); 21 | } 22 | 23 | BOOST_AUTO_TEST_CASE(testModOperSamePrec) { 24 | dec::decimal<2> b(2); 25 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(11.6) % b, 1.6); 26 | dec::decimal<2> c(2.1); 27 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(11.6) % c, 1.1); 28 | dec::decimal<2> d(1); 29 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-0.8) % d, -0.8); 30 | } 31 | 32 | BOOST_AUTO_TEST_CASE(testModOperLowerPrec) { 33 | dec::decimal<2> b(2); 34 | BOOST_CHECK_EQUAL(dec::decimal_cast<3>(11.6) % b, 1.6); 35 | dec::decimal<2> c(2.1); 36 | BOOST_CHECK_EQUAL(dec::decimal_cast<3>(11.6) % c, 1.1); 37 | } 38 | 39 | BOOST_AUTO_TEST_CASE(testModOperHigherPrec) { 40 | dec::decimal<3> b(2); 41 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(11.6) % b, 1.6); 42 | dec::decimal<3> c(2.1); 43 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(11.6) % c, 1.1); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /tests/decimalTestMult.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 7/19/21. 3 | // 4 | 5 | #include "decimal.h" 6 | #include 7 | 8 | BOOST_AUTO_TEST_CASE(decimalMultiplyPrec) 9 | { 10 | // check mult prec 11 | BOOST_CHECK_EQUAL(dec::decimal<4>("0.0001") * dec::decimal<4>("1.0000"), dec::decimal<4>("0.0001")); 12 | BOOST_CHECK_EQUAL(dec::decimal<4>("0.0010") * dec::decimal<4>("1.1000"), dec::decimal<4>("0.0011")); 13 | BOOST_CHECK_EQUAL(dec::decimal<4>("2.0100") * dec::decimal<4>("1.1000"), dec::decimal<4>("2.211")); 14 | BOOST_CHECK_EQUAL(dec::decimal<4>("2.5010") * dec::decimal<4>("1.5000"), dec::decimal<4>("3.7515")); 15 | 16 | // check mult neg 1 17 | BOOST_CHECK_EQUAL(dec::decimal<4>("-0.0001") * dec::decimal<4>("1.0000"), dec::decimal<4>("-0.0001")); 18 | BOOST_CHECK_EQUAL(dec::decimal<4>("-0.0010") * dec::decimal<4>("1.1000"), dec::decimal<4>("-0.0011")); 19 | BOOST_CHECK_EQUAL(dec::decimal<4>("-2.0100") * dec::decimal<4>("1.1000"), dec::decimal<4>("-2.211")); 20 | BOOST_CHECK_EQUAL(dec::decimal<4>("-2.5010") * dec::decimal<4>("1.5000"), dec::decimal<4>("-3.7515")); 21 | 22 | // check mult neg 2 23 | BOOST_CHECK_EQUAL(dec::decimal<4>("0.0001") * dec::decimal<4>("-1.0000"), dec::decimal<4>("-0.0001")); 24 | BOOST_CHECK_EQUAL(dec::decimal<4>("0.0010") * dec::decimal<4>("-1.1000"), dec::decimal<4>("-0.0011")); 25 | BOOST_CHECK_EQUAL(dec::decimal<4>("2.0100") * dec::decimal<4>("-1.1000"), dec::decimal<4>("-2.211")); 26 | BOOST_CHECK_EQUAL(dec::decimal<4>("2.5010") * dec::decimal<4>("-1.5000"), dec::decimal<4>("-3.7515")); 27 | 28 | // check mult both neg 29 | BOOST_CHECK_EQUAL(dec::decimal<4>("-0.0001") * dec::decimal<4>("-1.0000"), dec::decimal<4>("0.0001")); 30 | BOOST_CHECK_EQUAL(dec::decimal<4>("-0.0010") * dec::decimal<4>("-1.1000"), dec::decimal<4>("0.0011")); 31 | BOOST_CHECK_EQUAL(dec::decimal<4>("-2.0100") * dec::decimal<4>("-1.1000"), dec::decimal<4>("2.211")); 32 | BOOST_CHECK_EQUAL(dec::decimal<4>("-2.5010") * dec::decimal<4>("-1.5000"), dec::decimal<4>("3.7515")); 33 | 34 | // check medium prec - near 32 bits 35 | BOOST_CHECK_EQUAL(dec::decimal<9>("1.5") * dec::decimal<9>("2.1000"), dec::decimal<9>("3.15")); 36 | BOOST_CHECK_EQUAL(dec::decimal<9>("1.000000005") * dec::decimal<9>("2"), dec::decimal<9>("2.000000010")); 37 | BOOST_CHECK_EQUAL(dec::decimal<9>("1.80000001") * dec::decimal<9>("2"), dec::decimal<9>("3.600000020")); 38 | BOOST_CHECK_EQUAL(dec::decimal<9>("1.80000001") * dec::decimal<9>("2"), dec::decimal<9>("3.600000020")); 39 | 40 | // check as dec 6 41 | BOOST_CHECK_EQUAL(dec::decimal<6>("35000.000005") * dec::decimal<6>("54123.654133"), 42 | dec::decimal<6>("1894327894.925618")); 43 | } 44 | 45 | BOOST_AUTO_TEST_CASE(decimalMultiplyInt) 46 | { 47 | BOOST_CHECK_EQUAL(dec::decimal<4>("0.0001") * 2, dec::decimal<4>("0.0002")); 48 | BOOST_CHECK_EQUAL(dec::decimal<4>("0.1001") * 3, dec::decimal<4>("0.3003")); 49 | BOOST_CHECK_EQUAL(dec::decimal<4>("1.1001") * 3, dec::decimal<4>("3.3003")); 50 | BOOST_CHECK_EQUAL(dec::decimal<4>("1.0001") * 3, dec::decimal<4>("3.0003")); 51 | BOOST_CHECK_EQUAL(dec::decimal<4>("1.0") * 3, dec::decimal<4>("3.0")); 52 | 53 | BOOST_CHECK_EQUAL(dec::decimal<4>("0.0001") * -2, dec::decimal<4>("-0.0002")); 54 | BOOST_CHECK_EQUAL(dec::decimal<4>("0.1001") * -3, dec::decimal<4>("-0.3003")); 55 | BOOST_CHECK_EQUAL(dec::decimal<4>("1.1001") * -3, dec::decimal<4>("-3.3003")); 56 | BOOST_CHECK_EQUAL(dec::decimal<4>("1.0001") * -3, dec::decimal<4>("-3.0003")); 57 | BOOST_CHECK_EQUAL(dec::decimal<4>("1.0") * -3, dec::decimal<4>("-3.0")); 58 | 59 | BOOST_CHECK_EQUAL(dec::decimal<4>("-0.0001") * 2, dec::decimal<4>("-0.0002")); 60 | BOOST_CHECK_EQUAL(dec::decimal<4>("-0.1001") * 3, dec::decimal<4>("-0.3003")); 61 | BOOST_CHECK_EQUAL(dec::decimal<4>("-1.1001") * 3, dec::decimal<4>("-3.3003")); 62 | BOOST_CHECK_EQUAL(dec::decimal<4>("-1.0001") * 3, dec::decimal<4>("-3.0003")); 63 | BOOST_CHECK_EQUAL(dec::decimal<4>("-1.0") * 3, dec::decimal<4>("-3.0")); 64 | 65 | BOOST_CHECK_EQUAL(dec::decimal<4>("-0.0001") * -2, dec::decimal<4>("0.0002")); 66 | BOOST_CHECK_EQUAL(dec::decimal<4>("-0.1001") * -3, dec::decimal<4>("0.3003")); 67 | BOOST_CHECK_EQUAL(dec::decimal<4>("-1.1001") * -3, dec::decimal<4>("3.3003")); 68 | BOOST_CHECK_EQUAL(dec::decimal<4>("-1.0001") * -3, dec::decimal<4>("3.0003")); 69 | BOOST_CHECK_EQUAL(dec::decimal<4>("-1.0") * -3, dec::decimal<4>("3.0")); 70 | } 71 | 72 | -------------------------------------------------------------------------------- /tests/decimalTestMultDiv.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 7/19/21. 3 | // 4 | 5 | #include "decimal.h" 6 | #include 7 | 8 | class mult_div_tester { 9 | public: 10 | void test_md(dec::int64 a, dec::int64 b, dec::int64 divisor, dec::int64 expected) { 11 | using namespace dec; 12 | int64 res = dec_utils::multDiv(a, b, divisor); 13 | 14 | BOOST_CHECK_MESSAGE(res == expected, 15 | "multDiv(" << a << ", " << b << ", " << divisor << ") is " << res << ", expecting: " 16 | << expected); 17 | } 18 | }; 19 | 20 | BOOST_AUTO_TEST_CASE(multDiv) 21 | { 22 | 23 | { 24 | 25 | //test condition: 26 | // if ((value1 % divisor) == 0 || (value2 % divisor == 0)) { 27 | // return value1 * (value2 / divisor) + (value1 / divisor) * (value2 % divisor); 28 | // } 29 | 30 | mult_div_tester tester; 31 | 32 | tester.test_md(333, 425, 3, 47175); 33 | tester.test_md(425, 333, 3, 47175); 34 | 35 | tester.test_md(-333, 425, 3, -47175); 36 | tester.test_md(-425, 333, 3, -47175); 37 | 38 | tester.test_md(333, -425, 3, -47175); 39 | tester.test_md(425, -333, 3, -47175); 40 | 41 | tester.test_md(-333, -425, 3, 47175); 42 | tester.test_md(-425, -333, 3, 47175); 43 | 44 | //test condition: 45 | // if both modulo != 0 and no overflow in x = (value1 % divisor) * (value2 % divisor) then 46 | // add div_policy.div_rounded(x, divisor) to result and return 47 | 48 | tester.test_md(333, 424, 5, 28238); // round down 49 | tester.test_md(333, 593, 7, 28210); // round up 50 | 51 | tester.test_md(-333, 424, 5, -28238); 52 | tester.test_md(-333, 593, 7, -28210); 53 | 54 | tester.test_md(333, -424, 5, -28238); 55 | tester.test_md(333, -593, 7, -28210); 56 | 57 | tester.test_md(-333, -424, 5, 28238); 58 | tester.test_md(-333, -593, 7, 28210); 59 | 60 | // test step 3/4 - with overflow on x on decimal part but one of decimal parts has gcd with divisor > 1 61 | tester.test_md(438241312999, 3681227029158, 876482625990, 1840613514596); 62 | 63 | // test step 5 - overflow after gcd 64 | tester.test_md(438241312999, 3681227029158 + 222121, 876482625990, 1840613625656); 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /tests/decimalTestRoundOth.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 7/19/21. 3 | // 4 | 5 | #include "decimal.h" 6 | #include 7 | 8 | BOOST_AUTO_TEST_CASE(testFloor) { 9 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(2).floor(), 2); 10 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(2.4).floor(), 2); 11 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(2.9).floor(), 2); 12 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-2.7).floor(), -3); 13 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-2).floor(), -2); 14 | } 15 | 16 | BOOST_AUTO_TEST_CASE(testCeil) { 17 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(2).ceil(), 2); 18 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(2.4).ceil(), 3); 19 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(2.9).ceil(), 3); 20 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-2.7).ceil(), -2); 21 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-2).ceil(), -2); 22 | } 23 | 24 | BOOST_AUTO_TEST_CASE(testRound) { 25 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(2).round(), 2); 26 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(2.4).round(), 2); 27 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(2.9).round(), 3); 28 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-2.7).round(), -3); 29 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-2.4).round(), -2); 30 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-2).round(), -2); 31 | } 32 | 33 | BOOST_AUTO_TEST_CASE(testTrunc) { 34 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(2).trunc(), 2); 35 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(2.4).trunc(), 2); 36 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(2.9).trunc(), 2); 37 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-2.7).trunc(), -2); 38 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-2.4).trunc(), -2); 39 | BOOST_CHECK_EQUAL(dec::decimal_cast<2>(-2).trunc(), -2); 40 | } 41 | 42 | -------------------------------------------------------------------------------- /tests/decimalTestRounding.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 7/19/21. 3 | // 4 | 5 | #include "decimal.h" 6 | #include 7 | 8 | BOOST_AUTO_TEST_CASE(decimalRounding) 9 | { 10 | dec::decimal<2> balance; 11 | 12 | balance = dec::decimal2(3.156); 13 | BOOST_CHECK(balance == dec::decimal2(3.16)); 14 | 15 | balance = dec::decimal2("3.155"); 16 | BOOST_CHECK(balance == dec::decimal2(3.16)); 17 | 18 | balance = dec::decimal2(3.154); 19 | BOOST_CHECK(balance == dec::decimal2(3.15)); 20 | 21 | balance = dec::decimal2(-3.156); 22 | BOOST_CHECK(balance == dec::decimal2(-3.16)); 23 | 24 | balance = dec::decimal2("-3.155"); 25 | BOOST_CHECK(balance == dec::decimal2(-3.16)); 26 | 27 | balance = dec::decimal2(-3.154); 28 | BOOST_CHECK(balance == dec::decimal2(-3.15)); 29 | 30 | balance = dec::decimal2(3.67); 31 | balance = balance * dec::decimal2(3.67); 32 | BOOST_CHECK(balance == dec::decimal2(13.47)); 33 | 34 | balance = dec::decimal2(3.67); 35 | balance = balance / dec::decimal2(1.27); 36 | BOOST_CHECK(balance == dec::decimal2(2.89)); 37 | 38 | // test precision mixing 39 | balance = dec::decimal2(3.67); 40 | dec::decimal<6> crate(1.4567); 41 | balance = dec::decimal_cast<2>((dec::decimal_cast<6>(balance) * crate)); 42 | BOOST_CHECK(balance == dec::decimal2(5.35)); 43 | 44 | // test int overflow 45 | dec::decimal<10> largeValue(1311.12176161); 46 | balance = dec::decimal_cast<2>(largeValue * dec::decimal_cast<10>(10)); 47 | BOOST_CHECK(balance == dec::decimal2(13111.22)); 48 | 49 | // test serialization 50 | dec::decimal<12> longDec1("6321311.121761616789"); 51 | dec::decimal<12> longDec2; 52 | 53 | std::string s = toString(longDec1); 54 | longDec2 = dec::fromString >(s); 55 | 56 | BOOST_TEST_MESSAGE("longDec1: " << longDec1); 57 | BOOST_TEST_MESSAGE("longDec1 as string: " << s); 58 | BOOST_TEST_MESSAGE("longDec2: " << longDec2); 59 | BOOST_CHECK_EQUAL(longDec1, longDec2); 60 | } 61 | 62 | BOOST_AUTO_TEST_CASE(decimalDefRoundPolicy) 63 | { 64 | using namespace dec; 65 | decimal<1, def_round_policy> a("0.5"); 66 | decimal<1, def_round_policy> b("2"); 67 | decimal<1, def_round_policy> c; 68 | 69 | c = a / b; 70 | dec::decimal<1, def_round_policy> expected("0.3"); 71 | BOOST_CHECK(c == expected); 72 | } 73 | 74 | BOOST_AUTO_TEST_CASE(decimalNoRoundPolicy) 75 | { 76 | using namespace dec; 77 | decimal<1, null_round_policy> a("0.5"); 78 | decimal<1, null_round_policy> b("2"); 79 | decimal<1, null_round_policy> c; 80 | 81 | c = a / b; 82 | dec::decimal<1, null_round_policy> expected("0.2"); 83 | BOOST_CHECK(c == expected); 84 | } 85 | 86 | template 87 | class round_tester_t { 88 | public: 89 | round_tester_t(const std::string &name) : m_name(name) {} 90 | 91 | void test_assign(const std::string &src_value, const std::string &expected_value) { 92 | dec::decimal a; 93 | dec::decimal expected; 94 | 95 | a = dec::decimal(src_value); 96 | expected = dec::decimal(expected_value); 97 | 98 | BOOST_CHECK_MESSAGE(a == expected, 99 | "assign decimal<" << Prec << ", " << m_name << ">(" << src_value << ") is " << a 100 | << ", expecting: " << expected_value); 101 | } 102 | 103 | void test_div2(const std::string &src_value, const std::string &expected_value) { 104 | dec::decimal a; 105 | dec::decimal expected; 106 | 107 | a = dec::decimal(src_value) / dec::decimal("2.0"); 108 | expected = dec::decimal(expected_value); 109 | 110 | BOOST_CHECK_MESSAGE(a == expected, 111 | "div2 decimal<" << Prec << ", " << m_name << ">(" << src_value << ") is " << a 112 | << ", expecting: " << expected_value); 113 | } 114 | 115 | void test_div(const std::string &a_value, int b_value, const std::string &expected_value) { 116 | dec::decimal a; 117 | dec::decimal expected; 118 | 119 | a = dec::decimal(a_value) / dec::decimal_cast(b_value); 120 | expected = dec::decimal(expected_value); 121 | 122 | BOOST_CHECK_MESSAGE(a == expected, 123 | "divn decimal<" << Prec << ", " << m_name << ">(" << a_value << ", " << b_value << ") is " 124 | << a << ", expecting: " << expected_value); 125 | } 126 | 127 | private: 128 | std::string m_name; 129 | }; 130 | 131 | BOOST_AUTO_TEST_CASE(decimalRoundingPolicyOther) 132 | { 133 | using namespace dec; 134 | { 135 | round_tester_t<1, def_round_policy> tester("default"); 136 | 137 | tester.test_assign("0.16", "0.2"); 138 | tester.test_assign("0.15", "0.2"); 139 | tester.test_assign("0.14", "0.1"); 140 | 141 | tester.test_assign("0.06", "0.1"); 142 | tester.test_assign("0.05", "0.1"); 143 | tester.test_assign("0.04", "0.0"); 144 | 145 | tester.test_assign("-0.04", "-0.0"); 146 | tester.test_assign("-0.05", "-0.1"); 147 | tester.test_assign("-0.06", "-0.1"); 148 | 149 | tester.test_assign("-0.14", "-0.1"); 150 | tester.test_assign("-0.15", "-0.2"); 151 | tester.test_assign("-0.16", "-0.2"); 152 | 153 | tester.test_div("3.20", 20, "0.2"); 154 | tester.test_div("3.00", 20, "0.2"); 155 | tester.test_div("2.80", 20, "0.1"); 156 | 157 | tester.test_div("1.20", 20, "0.1"); 158 | tester.test_div("1.00", 20, "0.1"); 159 | tester.test_div("0.80", 20, "0.0"); 160 | 161 | tester.test_div("-0.80", 20, "-0.0"); 162 | tester.test_div("-1.00", 20, "-0.1"); 163 | tester.test_div("-1.20", 20, "-0.1"); 164 | 165 | tester.test_div("-2.80", 20, "-0.1"); 166 | tester.test_div("-3.00", 20, "-0.2"); 167 | tester.test_div("-3.20", 20, "-0.2"); 168 | } 169 | 170 | { 171 | round_tester_t<1, half_down_round_policy> tester("half_down"); 172 | 173 | tester.test_assign("0.16", "0.2"); 174 | tester.test_assign("0.15", "0.1"); 175 | tester.test_assign("0.14", "0.1"); 176 | 177 | tester.test_assign("0.06", "0.1"); 178 | tester.test_assign("0.05", "0.0"); 179 | tester.test_assign("0.04", "0.0"); 180 | 181 | tester.test_assign("-0.04", "0.0"); 182 | tester.test_assign("-0.05", "-0.1"); 183 | tester.test_assign("-0.06", "-0.1"); 184 | 185 | tester.test_assign("-0.14", "-0.1"); 186 | tester.test_assign("-0.15", "-0.2"); 187 | tester.test_assign("-0.16", "-0.2"); 188 | 189 | tester.test_div("3.20", 20, "0.2"); 190 | tester.test_div("3.00", 20, "0.1"); 191 | tester.test_div("2.80", 20, "0.1"); 192 | 193 | tester.test_div("1.20", 20, "0.1"); 194 | tester.test_div("1.00", 20, "0.0"); 195 | tester.test_div("0.80", 20, "0.0"); 196 | 197 | tester.test_div("-0.80", 20, "0.0"); 198 | tester.test_div("-1.00", 20, "-0.1"); 199 | tester.test_div("-1.20", 20, "-0.1"); 200 | 201 | tester.test_div("-2.80", 20, "-0.1"); 202 | tester.test_div("-3.00", 20, "-0.2"); 203 | tester.test_div("-3.20", 20, "-0.2"); 204 | } 205 | 206 | { 207 | round_tester_t<1, half_up_round_policy> tester("half_up"); 208 | 209 | tester.test_assign("0.16", "0.2"); 210 | tester.test_assign("0.15", "0.2"); 211 | tester.test_assign("0.14", "0.1"); 212 | 213 | tester.test_assign("0.06", "0.1"); 214 | tester.test_assign("0.05", "0.1"); 215 | tester.test_assign("0.04", "0.0"); 216 | 217 | tester.test_assign("-0.04", "0.0"); 218 | tester.test_assign("-0.05", "-0.0"); 219 | tester.test_assign("-0.06", "-0.1"); 220 | 221 | tester.test_assign("-0.14", "-0.1"); 222 | tester.test_assign("-0.15", "-0.1"); 223 | tester.test_assign("-0.16", "-0.2"); 224 | 225 | tester.test_div("3.20", 20, "0.2"); 226 | tester.test_div("3.00", 20, "0.2"); 227 | tester.test_div("2.80", 20, "0.1"); 228 | 229 | tester.test_div("1.20", 20, "0.1"); 230 | tester.test_div("1.00", 20, "0.1"); 231 | tester.test_div("0.80", 20, "0.0"); 232 | 233 | tester.test_div("-0.80", 20, "0.0"); 234 | tester.test_div("-1.00", 20, "-0.0"); 235 | tester.test_div("-1.20", 20, "-0.1"); 236 | 237 | tester.test_div("-2.80", 20, "-0.1"); 238 | tester.test_div("-3.00", 20, "-0.1"); 239 | tester.test_div("-3.20", 20, "-0.2"); 240 | } 241 | 242 | { 243 | round_tester_t<1, ceiling_round_policy> tester("ceiling"); 244 | 245 | tester.test_assign("0.16", "0.2"); 246 | tester.test_assign("0.15", "0.2"); 247 | tester.test_assign("0.14", "0.2"); 248 | 249 | tester.test_assign("0.06", "0.1"); 250 | tester.test_assign("0.05", "0.1"); 251 | tester.test_assign("0.04", "0.1"); 252 | 253 | tester.test_assign("-0.04", "0.0"); 254 | tester.test_assign("-0.05", "0.0"); 255 | tester.test_assign("-0.06", "0.0"); 256 | 257 | tester.test_assign("-0.14", "-0.1"); 258 | tester.test_assign("-0.15", "-0.1"); 259 | tester.test_assign("-0.16", "-0.1"); 260 | 261 | tester.test_div("3.20", 20, "0.2"); 262 | tester.test_div("3.00", 20, "0.2"); 263 | tester.test_div("2.80", 20, "0.2"); 264 | 265 | tester.test_div("1.20", 20, "0.1"); 266 | tester.test_div("1.00", 20, "0.1"); 267 | tester.test_div("0.80", 20, "0.1"); 268 | 269 | tester.test_div("-0.80", 20, "0.0"); 270 | tester.test_div("-1.00", 20, "0.0"); 271 | tester.test_div("-1.20", 20, "0.0"); 272 | 273 | tester.test_div("-2.80", 20, "-0.1"); 274 | tester.test_div("-3.00", 20, "-0.1"); 275 | tester.test_div("-3.20", 20, "-0.1"); 276 | } 277 | 278 | { 279 | round_tester_t<1, floor_round_policy> tester("floor"); 280 | 281 | tester.test_assign("0.16", "0.1"); 282 | tester.test_assign("0.15", "0.1"); 283 | tester.test_assign("0.14", "0.1"); 284 | 285 | tester.test_assign("0.06", "0.0"); 286 | tester.test_assign("0.05", "0.0"); 287 | tester.test_assign("0.04", "0.0"); 288 | 289 | tester.test_assign("-0.04", "-0.1"); 290 | tester.test_assign("-0.05", "-0.1"); 291 | tester.test_assign("-0.06", "-0.1"); 292 | 293 | tester.test_assign("-0.14", "-0.2"); 294 | tester.test_assign("-0.15", "-0.2"); 295 | tester.test_assign("-0.16", "-0.2"); 296 | 297 | tester.test_div("3.20", 20, "0.1"); 298 | tester.test_div("3.00", 20, "0.1"); 299 | tester.test_div("2.80", 20, "0.1"); 300 | 301 | tester.test_div("1.20", 20, "0.0"); 302 | tester.test_div("1.00", 20, "0.0"); 303 | tester.test_div("0.80", 20, "0.0"); 304 | 305 | tester.test_div("-0.80", 20, "-0.1"); 306 | tester.test_div("-1.00", 20, "-0.1"); 307 | tester.test_div("-1.20", 20, "-0.1"); 308 | 309 | tester.test_div("-2.80", 20, "-0.2"); 310 | tester.test_div("-3.00", 20, "-0.2"); 311 | tester.test_div("-3.20", 20, "-0.2"); 312 | } 313 | 314 | { 315 | round_tester_t<1, round_down_round_policy> tester("round-down"); 316 | 317 | tester.test_assign("0.16", "0.1"); 318 | tester.test_assign("0.15", "0.1"); 319 | tester.test_assign("0.14", "0.1"); 320 | 321 | tester.test_assign("0.06", "0.0"); 322 | tester.test_assign("0.05", "0.0"); 323 | tester.test_assign("0.04", "0.0"); 324 | 325 | tester.test_assign("-0.04", "0.0"); 326 | tester.test_assign("-0.05", "0.0"); 327 | tester.test_assign("-0.06", "0.0"); 328 | 329 | tester.test_assign("-0.14", "-0.1"); 330 | tester.test_assign("-0.15", "-0.1"); 331 | tester.test_assign("-0.16", "-0.1"); 332 | 333 | tester.test_div("3.20", 20, "0.1"); 334 | tester.test_div("3.00", 20, "0.1"); 335 | tester.test_div("2.80", 20, "0.1"); 336 | 337 | tester.test_div("1.2", 20, "0.0"); 338 | tester.test_div("1.0", 20, "0.0"); 339 | tester.test_div("0.8", 20, "0.0"); 340 | 341 | tester.test_div("-0.80", 20, "0.0"); 342 | tester.test_div("-1.00", 20, "0.0"); 343 | tester.test_div("-1.20", 20, "0.0"); 344 | 345 | tester.test_div("-2.80", 20, "-0.1"); 346 | tester.test_div("-3.00", 20, "-0.1"); 347 | tester.test_div("-3.20", 20, "-0.1"); 348 | } 349 | 350 | { 351 | round_tester_t<1, round_up_round_policy> tester("round-up"); 352 | 353 | tester.test_assign("0.16", "0.2"); 354 | tester.test_assign("0.15", "0.2"); 355 | tester.test_assign("0.14", "0.2"); 356 | 357 | tester.test_assign("0.06", "0.1"); 358 | tester.test_assign("0.05", "0.1"); 359 | tester.test_assign("0.04", "0.1"); 360 | 361 | tester.test_assign("-0.04", "-0.1"); 362 | tester.test_assign("-0.05", "-0.1"); 363 | tester.test_assign("-0.06", "-0.1"); 364 | 365 | tester.test_assign("-0.14", "-0.2"); 366 | tester.test_assign("-0.15", "-0.2"); 367 | tester.test_assign("-0.16", "-0.2"); 368 | 369 | tester.test_div("3.20", 20, "0.2"); 370 | tester.test_div("3.00", 20, "0.2"); 371 | tester.test_div("2.80", 20, "0.2"); 372 | 373 | tester.test_div("1.20", 20, "0.1"); 374 | tester.test_div("1.00", 20, "0.1"); 375 | tester.test_div("0.80", 20, "0.1"); 376 | 377 | tester.test_div("-0.80", 20, "-0.1"); 378 | tester.test_div("-1.00", 20, "-0.1"); 379 | tester.test_div("-1.20", 20, "-0.1"); 380 | 381 | tester.test_div("-2.80", 20, "-0.2"); 382 | tester.test_div("-3.00", 20, "-0.2"); 383 | tester.test_div("-3.20", 20, "-0.2"); 384 | } 385 | 386 | { 387 | round_tester_t<1, half_even_round_policy> tester("half-even"); 388 | 389 | tester.test_assign("0.16", "0.2"); 390 | tester.test_assign("0.15", "0.2"); 391 | tester.test_assign("0.14", "0.1"); 392 | 393 | tester.test_assign("0.06", "0.1"); 394 | tester.test_assign("0.05", "0.0"); 395 | tester.test_assign("0.04", "0.0"); 396 | 397 | tester.test_assign("-0.04", "0.0"); 398 | tester.test_assign("-0.05", "0.0"); 399 | tester.test_assign("-0.06", "-0.1"); 400 | 401 | tester.test_assign("-0.14", "-0.1"); 402 | tester.test_assign("-0.15", "-0.2"); 403 | tester.test_assign("-0.16", "-0.2"); 404 | 405 | tester.test_div("3.20", 20, "0.2"); 406 | tester.test_div("3.00", 20, "0.2"); 407 | tester.test_div("2.80", 20, "0.1"); 408 | 409 | tester.test_div("1.20", 20, "0.1"); 410 | tester.test_div("1.00", 20, "0.0"); 411 | tester.test_div("0.80", 20, "0.0"); 412 | 413 | tester.test_div("-0.80", 20, "0.0"); 414 | tester.test_div("-1.00", 20, "0.0"); 415 | tester.test_div("-1.20", 20, "-0.1"); 416 | 417 | tester.test_div("-2.80", 20, "-0.1"); 418 | tester.test_div("-3.00", 20, "-0.2"); 419 | tester.test_div("-3.20", 20, "-0.2"); 420 | } 421 | } 422 | 423 | -------------------------------------------------------------------------------- /tests/decimalTestString.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 7/19/21. 3 | // 4 | 5 | #include "decimal.h" 6 | #include 7 | 8 | BOOST_AUTO_TEST_CASE(decimalString) 9 | { 10 | using namespace dec; 11 | decimal<4> d(4.1234); 12 | std::string dTxt = toString(d); 13 | BOOST_CHECK(d == fromString >(dTxt)); 14 | 15 | // fromString converts with rounding 16 | BOOST_CHECK(d == decimal<4>().pack(4,1234)); 17 | BOOST_CHECK(d == fromString >("4.1234")); 18 | BOOST_CHECK(d == fromString >("4.12341")); 19 | BOOST_CHECK(d != fromString >("4.12345")); 20 | BOOST_CHECK(decimal<5>().pack(4,12341) == fromString >("4.12341")); 21 | 22 | // test negative values 23 | d = d * decimal_cast<4>(-1); 24 | BOOST_CHECK(d < decimal_cast<4>(0)); 25 | 26 | std::string sneg = toString(d); 27 | BOOST_TEST_MESSAGE("sneg: " << sneg); 28 | BOOST_TEST_MESSAGE("d: " << d); 29 | BOOST_TEST_MESSAGE("sneg-to-decimal: " << (fromString >(sneg))); 30 | BOOST_CHECK(d == fromString >(sneg)); 31 | } 32 | 33 | BOOST_AUTO_TEST_CASE(decimalToString) { 34 | 35 | std::string expected1 = "3.1549999999999998"; 36 | dec::decimal<16> d1(expected1); 37 | 38 | BOOST_CHECK_EQUAL(dec::toString(d1), expected1); 39 | 40 | std::string expected2 = "315499999999999.98"; 41 | dec::decimal<2> d2(expected2); 42 | 43 | BOOST_CHECK_EQUAL(dec::toString(d2), expected2); 44 | } 45 | 46 | BOOST_AUTO_TEST_CASE(decimalFromString) { 47 | 48 | std::string value1 = "3.1549999999999998"; 49 | dec::decimal<16> d1a (3.1549999999999998); 50 | dec::decimal<16> d1b(value1); 51 | 52 | BOOST_CHECK_EQUAL(d1a, d1b); 53 | 54 | std::string value2 = "31549999999999.98"; 55 | dec::decimal<2> d2a (31549999999999.98); 56 | dec::decimal<2> d2b(value2); 57 | 58 | BOOST_CHECK_EQUAL(d2a, d2b); 59 | } 60 | 61 | BOOST_AUTO_TEST_CASE(decimalToStringWithFormat) { 62 | 63 | dec::decimal_format format(',', '.'); 64 | 65 | std::string value1 = "3.1549999999999998"; 66 | std::string expected1 = "3,1549999999999998"; 67 | dec::decimal<16> d1(value1); 68 | 69 | BOOST_CHECK_EQUAL(dec::toString(d1, format), expected1); 70 | 71 | std::string value2 = "315499999999999.98"; 72 | std::string expected2 = "315.499.999.999.999,98"; 73 | dec::decimal<2> d2(value2); 74 | 75 | BOOST_CHECK_EQUAL(dec::toString(d2, format), expected2); 76 | } 77 | 78 | BOOST_AUTO_TEST_CASE(decimalFromStringWithFormat) { 79 | 80 | dec::decimal_format format(',', '.'); 81 | 82 | const std::string value1 = "3,1549999999999998"; 83 | dec::decimal<16> d1a (3.1549999999999998); 84 | dec::decimal<16> d1b(dec::fromString >(value1, format)); 85 | 86 | BOOST_CHECK_EQUAL(d1a, d1b); 87 | 88 | const std::string value2 = "31.549.999.999.999,98"; 89 | dec::decimal<2> d2a (31549999999999.98); 90 | dec::decimal<2> d2b(dec::fromString >(value2, format)); 91 | 92 | BOOST_CHECK_EQUAL(d2a, d2b); 93 | } 94 | 95 | struct italiano_separators : std::numpunct { 96 | char do_thousands_sep() const { return '.'; } // separate with dot 97 | std::string do_grouping() const { return "\3"; } // groups of 3 digits 98 | char do_decimal_point() const { return ','; } // separate with comma 99 | }; 100 | 101 | BOOST_AUTO_TEST_CASE(decimalItalianoParsing) { 102 | std::locale new_locale(std::cout.getloc(), new italiano_separators); 103 | std::locale prior_locale = std::locale::global(new_locale); 104 | std::stringstream ss("1.234,56"); 105 | dec::decimal<2> ret; 106 | dec::fromStream(ss, ret); 107 | std::locale::global(prior_locale); 108 | dec::DEC_INT64 beforeValue, afterValue; 109 | ret.unpack(beforeValue, afterValue); 110 | BOOST_CHECK_EQUAL(beforeValue, 1234); 111 | BOOST_CHECK_EQUAL(afterValue, 56); 112 | } 113 | 114 | BOOST_AUTO_TEST_CASE(decimalToStringWithGlobalCLocale) { 115 | std::locale prior_cout_locale = std::cout.getloc(); 116 | std::locale prior_locale = std::locale::global(std::locale("C")); 117 | 118 | dec::decimal<7> value = dec::decimal<7>::buildWithExponent(1234567890987L, -7); 119 | std::string expected = "123456.7890987"; 120 | 121 | BOOST_CHECK_EQUAL(dec::toString(value), expected); 122 | 123 | std::cout.imbue( prior_cout_locale ); 124 | std::locale::global(prior_locale); 125 | } 126 | 127 | BOOST_AUTO_TEST_CASE(decimalToStringWithGlobalItLocale) { 128 | std::locale prior_cout_locale = std::cout.getloc(); 129 | std::locale italiano_locale(std::cout.getloc(), new italiano_separators); 130 | std::locale prior_locale = std::locale::global(italiano_locale); 131 | 132 | dec::decimal<7> value = dec::decimal<7>::buildWithExponent(1234567890987L, -7); 133 | std::string expected = "123.456,7890987"; 134 | 135 | BOOST_CHECK_EQUAL(dec::toString(value), expected); 136 | 137 | std::cout.imbue(prior_cout_locale); 138 | std::locale::global(prior_locale); 139 | } 140 | 141 | BOOST_AUTO_TEST_CASE(decimalToStringWithGlobalItLocaleAndProvidedFormat) { 142 | std::locale prior_cout_locale = std::cout.getloc(); 143 | std::locale italiano_locale(std::cout.getloc(), new italiano_separators); 144 | std::locale prior_locale = std::locale::global(italiano_locale); 145 | 146 | dec::decimal_format format('.', '\0'); 147 | 148 | dec::decimal<7> value = dec::decimal<7>::buildWithExponent(1234567890987L, -7); 149 | std::string expected = "123456.7890987"; 150 | 151 | BOOST_CHECK_EQUAL(dec::toString(value, format), expected); 152 | 153 | std::cout.imbue(prior_cout_locale); 154 | std::locale::global(prior_locale); 155 | } 156 | 157 | BOOST_AUTO_TEST_CASE(decimalFromStringWithGlobalCLocale) { 158 | std::locale prior_cout_locale = std::cout.getloc(); 159 | std::locale prior_locale = std::locale::global(std::locale("C")); 160 | 161 | std::string value = "123456.7890987"; 162 | dec::decimal<7> expected = dec::decimal<7>::buildWithExponent(1234567890987L, -7); 163 | dec::decimal<7> d7(value); 164 | 165 | BOOST_CHECK_EQUAL(d7, expected); 166 | 167 | std::cout.imbue(prior_cout_locale); 168 | std::locale::global(prior_locale); 169 | } 170 | 171 | BOOST_AUTO_TEST_CASE(decimalFromStringWithGlobalItLocale) { 172 | std::locale prior_cout_locale = std::cout.getloc(); 173 | std::locale italiano_locale(std::cout.getloc(), new italiano_separators); 174 | std::locale prior_locale = std::locale::global(italiano_locale); 175 | 176 | std::string value = "123.456,7890987"; 177 | dec::decimal<7> expected = dec::decimal<7>::buildWithExponent(1234567890987L, -7); 178 | dec::decimal<7> d7(value); 179 | 180 | BOOST_CHECK_EQUAL(d7, expected); 181 | 182 | std::cout.imbue(prior_cout_locale); 183 | std::locale::global(prior_locale); 184 | } 185 | 186 | BOOST_AUTO_TEST_CASE(decimalFromStringWithGlobalItLocaleAndProvidedFormat) { 187 | std::locale prior_cout_locale = std::cout.getloc(); 188 | std::locale italiano_locale(std::cout.getloc(), new italiano_separators); 189 | std::locale prior_locale = std::locale::global(italiano_locale); 190 | 191 | dec::decimal_format cformat('.', '\0'); 192 | 193 | std::string value = "123456.7890987"; 194 | dec::decimal<7> expected = dec::decimal<7>::buildWithExponent(1234567890987L, -7); 195 | dec::decimal<7> d7(value, cformat); 196 | 197 | BOOST_CHECK_EQUAL(d7, expected); 198 | 199 | std::cout.imbue(prior_cout_locale); 200 | std::locale::global(prior_locale); 201 | } -------------------------------------------------------------------------------- /tests/decimalTestTypeLevel.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 7/19/21. 3 | // 4 | 5 | #include "decimal.h" 6 | #include 7 | 8 | BOOST_AUTO_TEST_CASE(decimalTypeLevel) 9 | { 10 | using namespace dec; 11 | dec::decimal<4> d4("-4.1234"); 12 | dec::decimal<8> d8("3.91726271"); 13 | 14 | // --- assignment 15 | 16 | #if DEC_TYPE_LEVEL >= 2 17 | d4 = d8; // will not compile on levels < 2 18 | BOOST_CHECK(d4 != decimal_cast<4>(0)); 19 | #endif 20 | #if DEC_TYPE_LEVEL >= 1 21 | d8 = d4; // will not compile on level 0 22 | BOOST_CHECK(d8 != decimal_cast<8>(0)); 23 | #endif 24 | 25 | // --- a + b 26 | 27 | #if DEC_TYPE_LEVEL >= 2 28 | d4 = d4 + d8; 29 | BOOST_CHECK(d4 != decimal_cast<4>(0)); 30 | #endif 31 | #if DEC_TYPE_LEVEL >= 1 32 | d8 = d8 + d4; 33 | BOOST_CHECK(d8 != decimal_cast<8>(0)); 34 | #endif 35 | 36 | // --- a += b 37 | 38 | #if DEC_TYPE_LEVEL >= 2 39 | d4 += d8; 40 | BOOST_CHECK(d4 != decimal_cast<4>(0)); 41 | #endif 42 | #if DEC_TYPE_LEVEL >= 1 43 | d8 += d4; 44 | BOOST_CHECK(d8 != decimal_cast<8>(0)); 45 | #endif 46 | 47 | // --- a - b 48 | 49 | #if DEC_TYPE_LEVEL >= 2 50 | d4 = d4 - d8; 51 | BOOST_CHECK(d4 != decimal_cast<4>(0)); 52 | #endif 53 | #if DEC_TYPE_LEVEL >= 1 54 | d8 = d8 - d4; 55 | BOOST_CHECK(d8 != decimal_cast<8>(0)); 56 | #endif 57 | 58 | // --- a -= b 59 | 60 | #if DEC_TYPE_LEVEL >= 2 61 | d4 -= d8; 62 | BOOST_CHECK(d4 != decimal_cast<4>(0)); 63 | #endif 64 | #if DEC_TYPE_LEVEL >= 1 65 | d8 -= d4; 66 | BOOST_CHECK(d8 != decimal_cast<8>(0)); 67 | #endif 68 | 69 | // --- a * b 70 | 71 | #if DEC_TYPE_LEVEL >= 2 72 | d4 = d4 * d8; 73 | BOOST_CHECK(d4 != decimal_cast<4>(0)); 74 | #endif 75 | #if DEC_TYPE_LEVEL >= 1 76 | d8 = d8 * d4; 77 | BOOST_CHECK(d8 != decimal_cast<8>(0)); 78 | #endif 79 | 80 | // --- a *= b 81 | 82 | #if DEC_TYPE_LEVEL >= 2 83 | d4 *= d8; 84 | BOOST_CHECK(d4 != decimal_cast<4>(0)); 85 | #endif 86 | #if DEC_TYPE_LEVEL >= 1 87 | d8 *= d4; 88 | BOOST_CHECK(d8 != decimal_cast<8>(0)); 89 | #endif 90 | 91 | // --- a / b 92 | 93 | #if DEC_TYPE_LEVEL >= 2 94 | d4 = d4 / d8; 95 | BOOST_CHECK(d4 != decimal_cast<4>(0)); 96 | #endif 97 | #if DEC_TYPE_LEVEL >= 1 98 | d8 = d8 / d4; 99 | BOOST_CHECK(d8 != decimal_cast<8>(0)); 100 | #endif 101 | 102 | // --- a /= b 103 | 104 | d4 = decimal_cast<4>(1234); 105 | d8 = decimal_cast<8>(3); 106 | 107 | #if DEC_TYPE_LEVEL >= 2 108 | d4 /= d8; 109 | BOOST_CHECK(d4 != decimal_cast<4>(0)); 110 | #endif 111 | #if DEC_TYPE_LEVEL >= 1 112 | d8 /= d4; 113 | BOOST_CHECK(d8 != decimal_cast<8>(0)); 114 | #endif 115 | } 116 | 117 | -------------------------------------------------------------------------------- /tests/decimalTestUtils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 2/24/24. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | std::string uint_to_string(unsigned int arg) { 9 | std::ostringstream out; 10 | out << arg; 11 | return (out.str()); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/decimalTestUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 2/24/24. 3 | // 4 | 5 | #ifndef DECIMAL_FOR_CPP_DECIMALTESTUTILS_H 6 | #define DECIMAL_FOR_CPP_DECIMALTESTUTILS_H 7 | 8 | std::string uint_to_string(unsigned int arg); 9 | 10 | #endif //DECIMAL_FOR_CPP_DECIMALTESTUTILS_H 11 | -------------------------------------------------------------------------------- /tests/decimalTestWithExponent.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by piotr on 7/19/21. 3 | // 4 | 5 | #include "decimal.h" 6 | #include 7 | 8 | BOOST_AUTO_TEST_CASE(decimalWithExponent) 9 | { 10 | // build positive values 11 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(11, 0), dec::decimal<4>("11")); 12 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(11, 2), dec::decimal<4>("1100")); 13 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(11, -2), dec::decimal<4>("0.11")); 14 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(11, 1), dec::decimal<4>("110")); 15 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(11, -1), dec::decimal<4>("1.1")); 16 | 17 | // build negative values 18 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(-11, 0), dec::decimal<4>("-11")); 19 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(-11, 2), dec::decimal<4>("-1100")); 20 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(-11, -2), dec::decimal<4>("-0.11")); 21 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(-11, 1), dec::decimal<4>("-110")); 22 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(-11, -1), dec::decimal<4>("-1.1")); 23 | 24 | // build zero 25 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(0, -1), dec::decimal<4>("0")); 26 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(0, 2), dec::decimal<4>("0")); 27 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(0, 0), dec::decimal<4>("0")); 28 | 29 | // add, get, set 30 | dec::decimal<4> temp; 31 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(temp, 111213, -3), dec::decimal<4>("111.213")); 32 | dec::decimal<4> a; 33 | a.setWithExponent(30, -2); 34 | temp += a; 35 | BOOST_CHECK_EQUAL(temp, dec::decimal<4>("111.5130")); 36 | 37 | dec::DEC_INT64 m; 38 | int e; 39 | temp.getWithExponent(m, e); 40 | BOOST_CHECK_EQUAL(m, 111513); 41 | BOOST_CHECK_EQUAL(e, -3); 42 | 43 | // rounding 44 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(temp, 111213, -5), dec::decimal<4>("1.1121")); 45 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(temp, 111215, -5), dec::decimal<4>("1.1122")); 46 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(temp, -111213, -5), dec::decimal<4>("-1.1121")); 47 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(temp, -111215, -5), dec::decimal<4>("-1.1122")); 48 | 49 | // check overflow 50 | BOOST_CHECK_EQUAL(dec::decimal<0>::buildWithExponent(1, 23), dec::decimal<0>("0")); 51 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(1, 23), dec::decimal<4>("0")); 52 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(1, 19), dec::decimal<4>("0")); 53 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(1, 15), dec::decimal<4>("0")); 54 | 55 | // check underflow 56 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(1, -19), dec::decimal<4>("0")); 57 | BOOST_CHECK_EQUAL(dec::decimal<4>::buildWithExponent(1, -15), dec::decimal<4>("0")); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /tests/runner.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | // Name: runner.cpp 3 | // Project: decimal 4 | // Purpose: Test runner for decimal library 5 | // Include it as the only .CPP file. 6 | // Author: Piotr Likus 7 | // Modified by: 8 | // Created: 01/02/2014 9 | // Licence: BSD 10 | ///////////////////////////////////////////////////////////////////////////// 11 | 12 | #ifdef __MINGW32__ 13 | // Mingw doesn't define putenv() needed by Boost.Test 14 | extern int putenv(char*); 15 | #endif 16 | 17 | #define BOOST_TEST_MAIN 18 | #define BOOST_TEST_MODULE "C++ Unit Tests for decimal" 19 | #include 20 | using namespace boost::unit_test; 21 | 22 | #include "decimal.h" 23 | 24 | BOOST_AUTO_TEST_CASE(decimalBasicTest) { 25 | using namespace dec; 26 | using namespace std; 27 | 28 | decimal<2> value1(143125); 29 | decimal<2> value2("143125"); 30 | 31 | BOOST_CHECK_EQUAL(value1, value2); 32 | } 33 | 34 | --------------------------------------------------------------------------------