From 18692d9baf31f78653a7ef48b4c1f174556f104e Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Sat, 10 Oct 2020 22:24:26 -0400 Subject: [PATCH 01/10] add random sqrt tests --- src/decn/decn.c | 2 +- src/decn/decn.h | 3 +++ src/decn/decn_tests.cpp | 50 ++++++++++++++++++++++++++++------------- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/decn/decn.c b/src/decn/decn.c index c44a87a..7b51bbd 100644 --- a/src/decn/decn.c +++ b/src/decn/decn.c @@ -149,7 +149,7 @@ exp_t get_exponent(const dec80* const x){ #endif } -static void set_exponent(dec80* acc, exp_t exponent, uint8_t num_is_neg){ +void set_exponent(dec80* acc, exp_t exponent, uint8_t num_is_neg){ #ifdef EXP16 if (num_is_neg){ exponent |= 0x8000; diff --git a/src/decn/decn.h b/src/decn/decn.h index 46f649e..22e8f48 100644 --- a/src/decn/decn.h +++ b/src/decn/decn.h @@ -60,6 +60,9 @@ typedef struct { //remove sign bit, and return 15 bit exponent sign-extended to 16 bits exp_t get_exponent(const dec80* const x); +void set_exponent(dec80* acc, exp_t exponent, uint8_t num_is_neg); + + void copy_decn(dec80* const dest, const dec80* const src); extern dec80 AccDecn; diff --git a/src/decn/decn_tests.cpp b/src/decn/decn_tests.cpp index 912a2ab..24965d9 100644 --- a/src/decn/decn_tests.cpp +++ b/src/decn/decn_tests.cpp @@ -21,6 +21,7 @@ #include +#include #include #include #include "decn.h" @@ -341,22 +342,18 @@ TEST_CASE("division"){ ); } -static void sqrt_test(const char* x_str, int x_exp) -{ - CAPTURE(x_str); CAPTURE(x_exp); - build_dec80(x_str, x_exp); - // decn_to_str_complete(&AccDecn); - // printf(" acc: %s\n", Buf); - sqrt_decn(); +static void sqrt_test(){ decn_to_str_complete(&AccDecn); - CAPTURE(Buf); // sqrt(x) - - //calculate actual result + CAPTURE(Buf); + //calculate result + sqrt_decn(); + //build mpfr float bmp::mpfr_float::default_precision(50); - std::string x_full_str(x_str); - x_full_str += "e" + std::to_string(x_exp); - CAPTURE(x_full_str); - bmp::mpfr_float x_actual(x_full_str); + bmp::mpfr_float x_actual(Buf); + //print calc result + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); + //calculate actual result CAPTURE(x_actual); if (decn_is_nan(&AccDecn)){ //check that NaN is from result of sqrt(-) @@ -366,12 +363,20 @@ static void sqrt_test(const char* x_str, int x_exp) CHECK(x_actual == 0); } else { x_actual = sqrt(x_actual); + CAPTURE(x_actual); bmp::mpfr_float calculated(Buf); bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual); - CHECK(rel_diff < 3e-16); //TODO + CHECK(rel_diff < 2e-17); } } +static void sqrt_test(const char* x_str, int x_exp) +{ + CAPTURE(x_str); CAPTURE(x_exp); + build_dec80(x_str, x_exp); + sqrt_test(); +} + TEST_CASE("sqrt"){ sqrt_test("0", 0); sqrt_test("2", 0); @@ -386,6 +391,21 @@ TEST_CASE("sqrt"){ sqrt_test("123", 12345); } +TEST_CASE("sqrt random"){ + std::default_random_engine generator; + std::uniform_int_distribution distribution(0,99); + std::uniform_int_distribution exp_distrib(-99,99); + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < 12345; j++){ + for (int i = 0; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distribution(generator); + } + int sign = sign_distrib(generator); + set_exponent(&AccDecn, exp_distrib(generator), sign); + sqrt_test(); + } +} + static void log_test( //input const char* x_str, int x_exp, From 89f442cae8164e9576a48eeb0f462c3807483599 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Sat, 10 Oct 2020 23:04:17 -0400 Subject: [PATCH 02/10] add random division/reciprocal tests --- src/decn/decn_tests.cpp | 72 +++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/src/decn/decn_tests.cpp b/src/decn/decn_tests.cpp index 24965d9..65b4897 100644 --- a/src/decn/decn_tests.cpp +++ b/src/decn/decn_tests.cpp @@ -259,6 +259,32 @@ TEST_CASE("multiply"){ CHECK_THAT(Buf, Equals("Error")); //acc*b } +static void div_test(){ //acc / b + bmp::mpf_float::default_precision(50); + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); + bmp::mpfr_float a_actual(Buf); + decn_to_str_complete(&BDecn); + CAPTURE(Buf); + bmp::mpfr_float b_actual(Buf); + //calc result + div_decn(); + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); // acc / b + + //calculate actual result + a_actual /= b_actual; + if (decn_is_nan(&AccDecn)){ + //check that NaN result of division by 0 + CAPTURE(a_actual); + CHECK(b_actual == 0); + } else { + bmp::mpfr_float calculated(Buf); + bmp::mpfr_float rel_diff = abs((a_actual - calculated) / a_actual); + CHECK(rel_diff < 2e-17); + } +} + static void div_test( //input const char* a_str, int a_exp, @@ -270,34 +296,7 @@ static void div_test( //do division build_dec80(a_str, a_exp); build_decn_at(&BDecn, b_str, b_exp); -// decn_to_str_complete(&AccDecn); -// printf(" acc: %s\n", Buf); -// decn_to_str_complete(&BDecn); -// printf(" b: %s\n", Buf); - div_decn(); - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); // acc / b - - //calculate actual result - bmp::mpfr_float::default_precision(50); - std::string a_full_str(a_str); - a_full_str += "e" + std::to_string(a_exp); - std::string b_full_str(b_str); - b_full_str += "e" + std::to_string(b_exp);; -// CAPTURE(a_full_str); -// CAPTURE(b_full_str); - bmp::mpfr_float a_actual(a_full_str); - bmp::mpfr_float b_actual(b_full_str); - a_actual /= b_actual; //calculate actual result - if (decn_is_nan(&AccDecn)){ - //check that NaN result of division by 0 - CAPTURE(a_actual); - CHECK(b_actual == 0); - } else { - bmp::mpfr_float calculated(Buf); - bmp::mpfr_float rel_diff = abs((a_actual - calculated) / a_actual); - CHECK(rel_diff < 1e-17); - } + div_test(); } TEST_CASE("division"){ @@ -342,6 +341,23 @@ TEST_CASE("division"){ ); } +TEST_CASE("division random"){ + std::default_random_engine gen; + std::uniform_int_distribution distrib(0, 99); + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < 12345; j++){ + AccDecn.lsu[0] = distrib(gen); + BDecn.lsu[0] = distrib(gen); + for (int i = 1; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + BDecn.lsu[i] = distrib(gen); + } + set_exponent(&AccDecn, distrib(gen), sign_distrib(gen)); + set_exponent(&BDecn, distrib(gen), sign_distrib(gen)); + div_test(); + } +} + static void sqrt_test(){ decn_to_str_complete(&AccDecn); CAPTURE(Buf); From 08f1d199c1e9d478aaad80584cfdb93fde48e221 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Mon, 12 Oct 2020 16:14:12 -0400 Subject: [PATCH 03/10] add random logarithm tests, extra tests near log(1.0) --- src/decn/decn.c | 2 +- src/decn/decn.h | 2 + src/decn/decn_tests.cpp | 165 ++++++++++++++++++++++++++++++++-------- 3 files changed, 136 insertions(+), 33 deletions(-) diff --git a/src/decn/decn.c b/src/decn/decn.c index 7b51bbd..53f5ae1 100644 --- a/src/decn/decn.c +++ b/src/decn/decn.c @@ -196,7 +196,7 @@ static void shift_left(dec80* x){ } } -static void remove_leading_zeros(dec80* x){ +void remove_leading_zeros(dec80* x){ uint8_t digit100; uint8_t is_negative = (x->exponent < 0); exp_t exponent = get_exponent(x); diff --git a/src/decn/decn.h b/src/decn/decn.h index 22e8f48..ea8b888 100644 --- a/src/decn/decn.h +++ b/src/decn/decn.h @@ -62,6 +62,8 @@ exp_t get_exponent(const dec80* const x); void set_exponent(dec80* acc, exp_t exponent, uint8_t num_is_neg); +void remove_leading_zeros(dec80* x); + void copy_decn(dec80* const dest, const dec80* const src); diff --git a/src/decn/decn_tests.cpp b/src/decn/decn_tests.cpp index 65b4897..174eca4 100644 --- a/src/decn/decn_tests.cpp +++ b/src/decn/decn_tests.cpp @@ -31,6 +31,8 @@ namespace bmp = boost::multiprecision; using Catch::Matchers::Equals; +static const int NUM_RAND_TESTS = 1234567; + TEST_CASE("build decn"){ build_dec80("0.0009234567890123456", 7); @@ -345,7 +347,7 @@ TEST_CASE("division random"){ std::default_random_engine gen; std::uniform_int_distribution distrib(0, 99); std::uniform_int_distribution sign_distrib(0,1); - for (int j = 0; j < 12345; j++){ + for (int j = 0; j < NUM_RAND_TESTS; j++){ AccDecn.lsu[0] = distrib(gen); BDecn.lsu[0] = distrib(gen); for (int i = 1; i < DEC80_NUM_LSU; i++){ @@ -412,7 +414,7 @@ TEST_CASE("sqrt random"){ std::uniform_int_distribution distribution(0,99); std::uniform_int_distribution exp_distrib(-99,99); std::uniform_int_distribution sign_distrib(0,1); - for (int j = 0; j < 12345; j++){ + for (int j = 0; j < NUM_RAND_TESTS; j++){ for (int i = 0; i < DEC80_NUM_LSU; i++){ AccDecn.lsu[i] = distribution(generator); } @@ -422,6 +424,94 @@ TEST_CASE("sqrt random"){ } } +static void log_test_(bool base10, double epsilon){ + bmp::mpfr_float::default_precision(50); + CAPTURE(base10); + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); + //build mpfr float + bmp::mpfr_float x_actual(Buf); + //calculate result + if (base10){ + log10_decn(); + } else { + ln_decn(); + } + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); // log(x) + + //calculate actual result + CAPTURE(x_actual); + if (decn_is_nan(&AccDecn)){ + //check that NaN is from result of log(-) + CHECK(x_actual <= 0); + } else { + if (base10){ + x_actual = log10(x_actual); + } else { + x_actual = log(x_actual); + } + bmp::mpfr_float calculated(Buf); + CAPTURE(calculated); + bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual); + CHECK(rel_diff < epsilon); + } +} + +static void log_test(bool base10=false){ + //check if near 1.0 + remove_leading_zeros(&AccDecn); + double lsu0 = AccDecn.lsu[0]; + int exp = get_exponent(&AccDecn); + if (exp == -1){ + lsu0 /= (double) 10; + lsu0 += (double) AccDecn.lsu[1] / (10*100); + lsu0 += (double) AccDecn.lsu[2] / (10*100*100); + lsu0 += (double) AccDecn.lsu[3] / (10*100*100*100); + } else if (exp == 0){ + lsu0 += (double) AccDecn.lsu[1] / 100; + lsu0 += (double) AccDecn.lsu[2] / (100*100); + lsu0 += (double) AccDecn.lsu[3] / (100*100*100); + } + CAPTURE((int) AccDecn.lsu[0]); CAPTURE((int) AccDecn.lsu[1]); + CAPTURE(exp); + CAPTURE(lsu0); + if (exp == 0 || exp == -1){ + //check if near 1.0 + if (lsu0 >= 7 && lsu0 < 8){ + log_test_(base10, 7.5e-16); + } else if (lsu0 >= 8 && lsu0 < 9){ + log_test_(base10, 1.5e-15); + } else if (lsu0 >= 9 && lsu0 < 9.6){ + log_test_(base10, 1.0e-14); + } else if (lsu0 >= 9.6 && lsu0 < 9.9){ + log_test_(base10, 4.1e-13); + } else if (lsu0 >= 9.9 && lsu0 < 9.999){ + log_test_(base10, 1.5e-11); + } else if (lsu0 >= 9.999 && lsu0 < 9.99999){ + log_test_(base10, 6.0e-10); + } else if (lsu0 >= 9.99999 && lsu0 < 9.9999999){ + log_test_(base10, 3.0e-9); + } else if (lsu0 >= 9.9999999 && lsu0 < 10.0){ + log_test_(base10, 1.3e-7); + } else if (lsu0 >= 10.0 && lsu0 < 10.00001){ + log_test_(base10, 6.0e-10); + } else if (lsu0 >= 10.00001 && lsu0 < 10.001){ + log_test_(base10, 6.0e-11); + } else if (lsu0 >= 10.001 && lsu0 < 10.1){ + log_test_(base10, 1.5e-12); + } else if (lsu0 >= 10.1 && lsu0 < 11){ + log_test_(base10, 1.6e-14); + } else if (lsu0 >= 11 && lsu0 < 13){ + log_test_(base10, 2.0e-15); + } else { + log_test_(base10, 6.5e-16); + } + } else { + log_test_(base10, 2e-16); + } +} + static void log_test( //input const char* x_str, int x_exp, @@ -431,36 +521,7 @@ static void log_test( CAPTURE(x_str); CAPTURE(x_exp); CAPTURE(base10); build_dec80(x_str, x_exp); - // decn_to_str_complete(&AccDecn); - // printf(" acc: %s\n", Buf); - if (base10){ - log10_decn(); - } else { - ln_decn(); - } - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); // log(x) - - //calculate actual result - bmp::mpfr_float::default_precision(50); - std::string x_full_str(x_str); - x_full_str += "e" + std::to_string(x_exp); - CAPTURE(x_full_str); - bmp::mpfr_float x_actual(x_full_str); - CAPTURE(x_actual); - if (decn_is_nan(&AccDecn)){ - //check that NaN is from result of log(-) - CHECK(x_actual <= 0); - } else { - if (base10){ - x_actual = log10(x_actual); - } else { - x_actual = log(x_actual); - } - bmp::mpfr_float calculated(Buf); - bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual); - CHECK(rel_diff < 3e-16); //TODO - } + log_test(base10); } TEST_CASE("log"){ @@ -476,6 +537,46 @@ TEST_CASE("log"){ log_test("123", 12345); } +TEST_CASE("log random"){ + std::default_random_engine gen; + std::uniform_int_distribution distrib(0,99); + std::uniform_int_distribution exp_distrib(-99,99); + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < NUM_RAND_TESTS; j++){ + for (int i = 0; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + } + int exp = exp_distrib(gen); + set_exponent(&AccDecn, exp, 0); + int base10 = sign_distrib(gen); + log_test(base10); + } +} + +static void log_test_near1(int lsu0_low, int lsu0_high, int exp){ + std::default_random_engine gen; + std::uniform_int_distribution lsu0_distrib(lsu0_low, lsu0_high); + std::uniform_int_distribution distrib(0,99); + std::uniform_int_distribution exp_distrib(-99,99); + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < NUM_RAND_TESTS; j++){ + AccDecn.lsu[0] = lsu0_distrib(gen); + for (int i = 1; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + } + set_exponent(&AccDecn, exp, 0); + int base10 = sign_distrib(gen); + log_test(base10); + } +} + +TEST_CASE("log random near 1"){ + log_test_near1(0, 99, -1); + log_test_near1(80, 99, -1); + log_test_near1(10, 99, 0); + log_test_near1(10, 20, 0); +} + static void exp_test( //input const char* x_str, int x_exp, From 46851eab9fbaf73a99d7c5a45a57d1f73ceb8678 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Mon, 12 Oct 2020 16:15:39 -0400 Subject: [PATCH 04/10] use ctest to run unit tests in parallel (requires newer catch2) --- .github/workflows/main.yml | 2 ++ CMakeLists.txt | 13 +++++++----- Dockerfile | 9 ++++++-- src/decn/CMakeLists.txt | 40 +++++++++++++++++++++++++----------- src/decn/catch_main.cpp | 2 +- src/decn/decn_tests.cpp | 12 ++++++++--- src/decn/decn_tests_trig.cpp | 2 +- steps/desktop_build_check.sh | 6 ++++-- 8 files changed, 60 insertions(+), 26 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7332049..be20ff8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,5 +25,7 @@ jobs: ${{ github.workspace }}/build/ ${{ github.workspace }}/build_qt/lcov/ ${{ github.workspace }}/build_qt/decn.c.gcov + ${{ github.workspace }}/build_qt/Testing/ ${{ github.workspace }}/main.hex if-no-files-found: error + diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b77b3c..cf0c4d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,20 +4,23 @@ project(stc_rpncalc C CXX) find_package(Qt5 COMPONENTS Widgets Qml Quick REQUIRED) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") -message(STATUS "using address sanitizer") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls") -link_libraries(asan) + message(STATUS "using address sanitizer") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls") + link_libraries(asan) endif() set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type (for tests debug make sense)") # Compiler warnings if(MSVC) - add_compile_options(/W4 /WX) + add_compile_options(/W4 /WX) else() - add_compile_options(-Wall -Wextra -pedantic) + add_compile_options(-Wall -Wextra -pedantic) endif() +# CTest Catch2 tests +enable_testing() + # Directory with source code add_subdirectory(src) add_subdirectory(qt_gui) diff --git a/Dockerfile b/Dockerfile index 253b0b0..123f28c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,6 @@ FROM ubuntu:18.04 RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ build-essential \ - catch \ clang \ cmake \ git \ @@ -13,4 +12,10 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ ninja-build \ qtdeclarative5-dev \ sdcc=3.5.0+dfsg-2build1 \ - vim-tiny + vim-tiny \ + wget + +# install more up-to-date catch2 +RUN wget http://mirrors.kernel.org/ubuntu/pool/universe/c/catch2/catch2_2.13.0-1_all.deb +RUN echo "1d501c7f817cfcd46dd1b79edc10896d catch2_2.13.0-1_all.deb" | md5sum --check -- +RUN dpkg -i catch2_2.13.0-1_all.deb diff --git a/src/decn/CMakeLists.txt b/src/decn/CMakeLists.txt index 77ef315..ed8b7a7 100644 --- a/src/decn/CMakeLists.txt +++ b/src/decn/CMakeLists.txt @@ -1,11 +1,3 @@ -add_library(Catch INTERFACE) -if(EXISTS /usr/include/catch/catch.hpp) -target_include_directories(Catch INTERFACE /usr/include/catch) -elseif(EXISTS /usr/include/catch2/catch.hpp) -target_include_directories(Catch INTERFACE /usr/include/catch2) -else() -endif() - #code coverage add_library(coverage_config INTERFACE) target_compile_options(coverage_config INTERFACE -O0 -g --coverage) @@ -18,11 +10,35 @@ add_library(decn decn.c ../utils.c) add_library(decn_cover decn.c) target_link_libraries(decn_cover PUBLIC coverage_config) -add_executable(decn_test decn_test.c ../utils.c) -target_link_libraries(decn_test decn_cover coverage_config Catch) +# old tests (compare output with reference "golden" output file) +add_executable(decn_test + decn_test.c + ../utils.c +) +target_link_libraries(decn_test + decn_cover + coverage_config +) -add_executable(decn_tests catch_main.cpp decn_tests.cpp decn_tests_trig.cpp ../utils.c) -target_link_libraries(decn_tests decn_cover coverage_config mpfr Catch) +# catch2 unit tests +find_package(Catch2 REQUIRED) +enable_testing() +set (BUILD_TESTING ON) +add_executable(decn_tests + catch_main.cpp + decn_tests.cpp + decn_tests_trig.cpp + ../utils.c +) +target_link_libraries(decn_tests + mpfr + decn_cover + coverage_config + Catch2::Catch2 +) +include(CTest) +include(Catch) +catch_discover_tests(decn_tests) # decn prototyping add_subdirectory(proto) diff --git a/src/decn/catch_main.cpp b/src/decn/catch_main.cpp index f930f49..54bc23c 100644 --- a/src/decn/catch_main.cpp +++ b/src/decn/catch_main.cpp @@ -21,4 +21,4 @@ #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file -#include "catch.hpp" +#include diff --git a/src/decn/decn_tests.cpp b/src/decn/decn_tests.cpp index 174eca4..6db4d6a 100644 --- a/src/decn/decn_tests.cpp +++ b/src/decn/decn_tests.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include "decn.h" #include "../utils.h" @@ -31,7 +31,7 @@ namespace bmp = boost::multiprecision; using Catch::Matchers::Equals; -static const int NUM_RAND_TESTS = 1234567; +static const int NUM_RAND_TESTS = 123456; TEST_CASE("build decn"){ @@ -570,10 +570,16 @@ static void log_test_near1(int lsu0_low, int lsu0_high, int exp){ } } -TEST_CASE("log random near 1"){ +TEST_CASE("log random 0 to 0.99..."){ log_test_near1(0, 99, -1); +} +TEST_CASE("log random 0.8 to 0.99..."){ log_test_near1(80, 99, -1); +} +TEST_CASE("log random 1.0 to 9.9"){ log_test_near1(10, 99, 0); +} +TEST_CASE("log random 1.0 to 2.0"){ log_test_near1(10, 20, 0); } diff --git a/src/decn/decn_tests_trig.cpp b/src/decn/decn_tests_trig.cpp index 534e7bf..c8b53a6 100644 --- a/src/decn/decn_tests_trig.cpp +++ b/src/decn/decn_tests_trig.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include "decn.h" namespace bmp = boost::multiprecision; using Catch::Matchers::Equals; diff --git a/steps/desktop_build_check.sh b/steps/desktop_build_check.sh index 94601a4..80aadd7 100755 --- a/steps/desktop_build_check.sh +++ b/steps/desktop_build_check.sh @@ -17,11 +17,13 @@ cmake .. -GNinja ninja # run tests -src/decn/decn_tests +ctest -j $(nproc) # get coverage echo "Running lcov" lcov --capture --directory src/decn --output-file coverage.info +lcov --remove coverage.info "/usr/*" --output-file coverage.info genhtml coverage.info --output-directory lcov echo "Running gcov" -gcov -b src/decn/CMakeFiles/decn_cover.dir/decn.c.gcno \ No newline at end of file +gcov -b src/decn/CMakeFiles/decn_cover.dir/decn.c.gcno + From 714cd69b9c52f06a28aa511377df3852593e468b Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Tue, 13 Oct 2020 00:49:52 -0400 Subject: [PATCH 05/10] add random exp() tests --- src/decn/decn_tests.cpp | 115 +++++++++++++++++++++++++++++++++------- 1 file changed, 95 insertions(+), 20 deletions(-) diff --git a/src/decn/decn_tests.cpp b/src/decn/decn_tests.cpp index 6db4d6a..e0e18fe 100644 --- a/src/decn/decn_tests.cpp +++ b/src/decn/decn_tests.cpp @@ -583,16 +583,15 @@ TEST_CASE("log random 1.0 to 2.0"){ log_test_near1(10, 20, 0); } -static void exp_test( - //input - const char* x_str, int x_exp, - double epsilon=6e-16, - bool base10=false -) -{ - CAPTURE(x_str); CAPTURE(x_exp); +static void exp_test_(bool base10, double epsilon){ + bmp::mpfr_float::default_precision(50); CAPTURE(base10); - build_dec80(x_str, x_exp); + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); //x + CAPTURE(AccDecn.exponent); + //build mpfr float + bmp::mpfr_float x_actual(Buf); + //calculate result if (base10){ exp10_decn(); } else { @@ -600,14 +599,9 @@ static void exp_test( } decn_to_str_complete(&AccDecn); CAPTURE(Buf); // exp(x) - CAPTURE(AccDecn.exponent); //calculate actual result - bmp::mpfr_float::default_precision(50); bmp::mpfr_float calculated(Buf); - std::string x_full_str(x_str); - x_full_str += "e" + std::to_string(x_exp); - bmp::mpfr_float x_actual(x_full_str); if (base10){ x_actual *= log(10); } @@ -617,13 +611,60 @@ static void exp_test( CHECK(rel_diff < epsilon); } -static void exp10_test( +static void exp_test(bool base10=false){ + double x; + int exp = get_exponent(&AccDecn); + if (exp == 1){ + x = AccDecn.lsu[0]; + x += (double) AccDecn.lsu[1] / 100; + } else if (exp == 2){ + x = (double) AccDecn.lsu[0] * 10; + x += (double) AccDecn.lsu[1] / 10; + } + CAPTURE((int) AccDecn.lsu[0]); CAPTURE((int) AccDecn.lsu[1]); + CAPTURE(exp); + CAPTURE(x); + double epsilon; + if (exp == 1 || exp == 2){ + if (x > 230){ + epsilon = 8e-15; + } else if (x > 210){ + epsilon = 6e-15; + } else if (x > 180){ + epsilon = 5e-15; + } else if (x > 150){ + epsilon = 4e-15; + } else if (x > 125){ + epsilon = 3e-15; + } else if (x > 100){ + epsilon = 2e-15; + } else if (x > 65){ + epsilon = 1e-15; + } + } else { + epsilon = 6e-16; + } + CAPTURE(base10); + if (base10){ + epsilon *= 20; + } + exp_test_(base10, epsilon); +} + +static void exp_test( //input const char* x_str, int x_exp, - double epsilon=3e-15 + bool base10=false ) { - exp_test(x_str, x_exp, epsilon, true); + CAPTURE(x_str); CAPTURE(x_exp); + CAPTURE(base10); + build_dec80(x_str, x_exp); + exp_test(base10); +} + +static void exp10_test(const char* x_str, int x_exp){ + exp_test(x_str, x_exp, true); } TEST_CASE("exp"){ @@ -632,11 +673,14 @@ TEST_CASE("exp"){ exp_test("9.999", 0); exp_test("10", 0); exp_test("10.001", 0); - exp_test("2.3", 2, 6e-15); + exp_test("2.3", 2);//, 6e-15); exp_test("2.02", -10); exp_test("2.02", 0); exp_test("1.5", 0); - exp_test("294.69999999", 0, 8e-15); + exp_test("99.999999", 0); + exp_test("230.2", 0);//, 6e-15); + exp_test("-230", 0);//, 6e-15); + exp_test("294.69999999", 0);//, 8e-15); //do not operate on NaN set_dec80_NaN(&AccDecn); @@ -654,7 +698,38 @@ TEST_CASE("exp10"){ exp10_test("2.02", -10); exp10_test("2.02", 0); exp10_test("1.5", 0); - exp10_test("127", 0, 3e-14); + exp10_test("127", 0);//, 3e-14); + exp10_test("99.999999", 0);//, 2e-14); +} + +static void test_exp_random(int exp_distrib_low){ + std::default_random_engine gen; + std::uniform_int_distribution distrib(0, 99); + std::uniform_int_distribution lsu0_high_distrib(0, 23); + std::uniform_int_distribution exp_distrib(exp_distrib_low, 2); + std::uniform_int_distribution sign_distrib(0, 1); + for (int j = 0; j < NUM_RAND_TESTS; j++){ + int exp = exp_distrib(gen); + int sign = sign_distrib(gen); + if (exp == 2) { + //limit x to approximately +/- 230 + AccDecn.lsu[0] = lsu0_high_distrib(gen); + } else { + AccDecn.lsu[0] = distrib(gen); + } + for (int i = 1; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + } + set_exponent(&AccDecn, exp, sign); + exp_test(); + } +} + +TEST_CASE("exp random"){ + test_exp_random(-99); +} +TEST_CASE("exp large random"){ + test_exp_random(1); } static void pow_test( From d3cddc13268a786e927017f7009d76f5dada44f2 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Tue, 13 Oct 2020 01:40:51 -0400 Subject: [PATCH 06/10] update readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e17c600..2c7e4eb 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ The keys on the *original* calculator map as follows: - acts as acos(x) when shifted down - `3 `: acts as tan(x) when shifted - acts as atan(x) when shifted down -- all trig functions are currently calculated in degrees +- all trig functions are currently calculated in radians (TODO: change to degrees by default) - `- `: acts as to radians when shifted - acts as to degrees when shifted down - `+ `: acts as LastX when shifted @@ -119,7 +119,7 @@ Github releases has prebuilt binaries for the calculator. Building is fairly str - See https://sourceforge.net/p/sdcc/discussion/1865/thread/9589cc8d57/ - Luckily SDCC has few dependencies, and older versions can be installed fairly easily. - CMakeLists.txt is for building the Qt desktop application, and also the decimal-number-library test application. - - build similarly to other cmake projects: + - build similarly to other cmake projects, see [Dockerfile](Dockerfile) for build dependencies: - `mkdir build_qt && cd build_qt` - `cmake -DCMAKE_BUILD_TYPE=Debug -G "Eclipse CDT4 - Ninja" ..` - (you can choose a different generator, I prefer using Ninja to build, because it's fast) @@ -261,7 +261,7 @@ The original firmware that came with this calculator used a fixed-point format, This replacement calculator firmware uses decimal floating point, using base-100 to store numbers and do calculations. Base-100 allows for efficient storage into 8-bit bytes, and is easier to work with than packed-BCD. Unlike straight binary representations, base-100 is still fairly easy to display as decimal. Also unlike binary representations, there is no conversion error from binary/decimal (e.g. numbers like `0.1` can be represented exactly). -Each `uint8_t` stores a base-100 "`digit100`", referred to as an "`lsu`", for least significant unit. (The LSU terminology is borrowed from the decNumber library: I originally considered using the decNumber library similar to the WP-34S calculator, but just the library itself takes several times more flash than is available on this calculator. I also considered using the BigNu mber arduino library, but that library uses C++ and lots of pointers passed to functions, which are extremely expensive on the 8051 architecture.) The number format is as follows: +Each `uint8_t` stores a base-100 "`digit100`", referred to as an "`lsu`", for least significant unit. (The LSU terminology is borrowed from the decNumber library: I originally considered using the decNumber library similar to the WP-34S calculator, but just the library itself takes several times more flash than is available on this calculator. I also considered using the BigNumber arduino library, but that library uses C++ and lots of pointers passed to functions, which are extremely expensive on the 8051 architecture.) The number format is as follows: - `lsu[0]`: contains the most significant `digit100` (the most significant 2 decimal digits) - implicit decimal point between `lsu[0]/10` and `lsu[0]%10` @@ -322,7 +322,7 @@ The number `0.135` would be stored the same way, except now the exponent is `0x7 The keyboard matrix is scanned once every 5ms. The keyboard debouncing is based on the quick draw/integrator hybrid algorithm described [here](https://summivox.wordpress.com/2016/06/03/keyboard-matrix-scanning-and-debouncing/). This algorithm combines the advantages of both methods: 1. It signals a key press immediately, the very first instant a keyboard matrix scan detects a key is pressed (similar to the "quick-draw" method). -1. It has an "integrator" to determine both when a key is fully pressed and when a key is fully released. This prevents the mechanically bouncy keys from registering multiple times when pressed. +1. It has an "integrator" (a saturating up/down counter) to determine both when a key is fully pressed and when a key is fully released. This prevents the mechanically bouncy keys from registering multiple times when pressed. In practice, the keyboard debouncing works much better than the original firmware (which would occasionally miss keystrokes). From 540d9e282ca16ceca2e706ae24eed7bbdaf398c6 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Mon, 19 Oct 2020 21:36:18 -0400 Subject: [PATCH 07/10] add random y^x tests --- src/decn/decn_tests.cpp | 124 ++++++++++++++++++++++++++++++++-------- 1 file changed, 99 insertions(+), 25 deletions(-) diff --git a/src/decn/decn_tests.cpp b/src/decn/decn_tests.cpp index e0e18fe..e5e48e9 100644 --- a/src/decn/decn_tests.cpp +++ b/src/decn/decn_tests.cpp @@ -732,6 +732,52 @@ TEST_CASE("exp large random"){ test_exp_random(1); } +static void pow_test(){ // a^b + bmp::mpf_float::default_precision(50); + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); // a + bmp::mpfr_float a_actual(Buf); + decn_to_str_complete(&BDecn); + CAPTURE(Buf); // b + bmp::mpfr_float b_actual(Buf); + //calculate result + pow_decn(); + //calculate actual result + bmp::mpfr_float res_actual(pow(a_actual, b_actual)); + //check overflow or underflow + if (decn_is_nan(&AccDecn)){ + //check overflow or underflow + if (b_actual > 0) { + CHECK(log(res_actual) > 100); + } else { + CHECK(log(res_actual) < -100); + } + return; + } + //not over/underflow, get string and log calculated result + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); // a^b + bmp::mpfr_float calculated(Buf); + //check relative error + double rel_tol = 4.5e-14; + if (a_actual > 1.0 && a_actual < 1.0001){ + rel_tol = 1e-7; + } else if (a_actual > 0.9 && a_actual < 2.0){ + rel_tol = 1.5e-10; + } else if (log(res_actual) > 100){ + rel_tol = 1e-12; + } + CAPTURE(a_actual); + CAPTURE(rel_tol); + if (decn_is_zero(&AccDecn)) { + bmp::mpfr_float diff = abs(res_actual - calculated); + CHECK(diff < rel_tol); + } else { + bmp::mpfr_float rel_diff = abs((res_actual - calculated)/res_actual); + CHECK(rel_diff < rel_tol); + } +} + static void pow_test( //input const char* a_str, int a_exp, @@ -744,31 +790,7 @@ static void pow_test( //compute power build_decn_at(&BDecn, b_str, b_exp); build_dec80(a_str, a_exp); - - pow_decn(); - - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); // a^b - - //calculate actual result - bmp::mpfr_float::default_precision(50); - bmp::mpfr_float calculated(Buf); - std::string a_full_str(a_str); - a_full_str += "e" + std::to_string(a_exp); - std::string b_full_str(b_str); - b_full_str += "e" + std::to_string(b_exp);; - // CAPTURE(a_full_str); - // CAPTURE(b_full_str); - bmp::mpfr_float a_actual(a_full_str); - bmp::mpfr_float b_actual(b_full_str); - a_actual = pow(a_actual, b_actual); - if (decn_is_zero(&AccDecn)) { - bmp::mpfr_float diff = abs(a_actual - calculated); - CHECK(diff < 3e-14); - } else { - bmp::mpfr_float rel_diff = abs((a_actual - calculated)/a_actual); - CHECK(rel_diff < 3e-14); - } + pow_test(); } TEST_CASE("power"){ @@ -803,6 +825,58 @@ TEST_CASE("power"){ ); } +static void power_test(int lsu0_low, int lsu0_high, int exp_low=-99, int exp_high=99){ + std::default_random_engine gen; + std::uniform_int_distribution lsu0_distrib(lsu0_low, lsu0_high); + std::uniform_int_distribution distrib(0, 99); + std::uniform_int_distribution exp_distrib(exp_low, exp_high); + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < NUM_RAND_TESTS; j++){ + AccDecn.lsu[0] = lsu0_distrib(gen); + for (int i = 1; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + BDecn.lsu[i] = distrib(gen); + } + set_exponent(&AccDecn, exp_distrib(gen), 0); + //generate exponent for b to minimize chance of a^b overflowing: + // a^b <= 1e100 + // b*log(a) <= log(1e100) = 100 + // b <= 100/log(a) + // b_exponent <= log(100/log(a)) = log(100) - log(log(a)) + // b_exponent <= 2 - log(log(a)) + decn_to_str_complete(&AccDecn); + bmp::mpfr_float acc(Buf); + acc = 2.0 - log(log(acc)); + double b_exponent_high_flt = acc.convert_to(); + int b_exponent_high = b_exponent_high_flt; + int b_exponent_low = -99; + //ensure b_exponent high in range + if (b_exponent_high > 99){ + b_exponent_high = 99; + } else if (b_exponent_high < b_exponent_low){ + b_exponent_high = b_exponent_low; + } + CAPTURE(b_exponent_low); + CAPTURE(b_exponent_high); + std::uniform_int_distribution b_exp_distrib(b_exponent_low, b_exponent_high); + int b_exponent = b_exp_distrib(gen); + CAPTURE(b_exponent); + int b_neg = sign_distrib(gen); + set_exponent(&BDecn, b_exponent, b_neg); + pow_test(); + } +} + +TEST_CASE("power random"){ + power_test(0, 99); +} +TEST_CASE("power random 0.9 to 0.99..."){ + power_test(90, 99, -1, -1); +} +TEST_CASE("power random 1.0 to 2.0..."){ + power_test(10, 20, 0, 0); +} + TEST_CASE("u32str corner"){ u32str(0, &Buf[0], 10); CHECK_THAT(Buf, Equals("0")); From 81253d8934a058f6470bb05712c2cb07a55ddda1 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Mon, 26 Oct 2020 22:16:26 -0400 Subject: [PATCH 08/10] separate out tests into separate files --- src/decn/CMakeLists.txt | 2 + src/decn/decn_tests.cpp | 620 +------------------------ src/decn/decn_tests.h | 28 ++ src/decn/decn_tests_div_sqrt.cpp | 200 ++++++++ src/decn/decn_tests_transcendental.cpp | 490 +++++++++++++++++++ 5 files changed, 722 insertions(+), 618 deletions(-) create mode 100644 src/decn/decn_tests.h create mode 100644 src/decn/decn_tests_div_sqrt.cpp create mode 100644 src/decn/decn_tests_transcendental.cpp diff --git a/src/decn/CMakeLists.txt b/src/decn/CMakeLists.txt index ed8b7a7..d627d94 100644 --- a/src/decn/CMakeLists.txt +++ b/src/decn/CMakeLists.txt @@ -27,6 +27,8 @@ set (BUILD_TESTING ON) add_executable(decn_tests catch_main.cpp decn_tests.cpp + decn_tests_div_sqrt.cpp + decn_tests_transcendental.cpp decn_tests_trig.cpp ../utils.c ) diff --git a/src/decn/decn_tests.cpp b/src/decn/decn_tests.cpp index e5e48e9..550d993 100644 --- a/src/decn/decn_tests.cpp +++ b/src/decn/decn_tests.cpp @@ -27,12 +27,12 @@ #include "decn.h" #include "../utils.h" +#include "decn_tests.h" + namespace bmp = boost::multiprecision; using Catch::Matchers::Equals; -static const int NUM_RAND_TESTS = 123456; - TEST_CASE("build decn"){ build_dec80("0.0009234567890123456", 7); @@ -261,622 +261,6 @@ TEST_CASE("multiply"){ CHECK_THAT(Buf, Equals("Error")); //acc*b } -static void div_test(){ //acc / b - bmp::mpf_float::default_precision(50); - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); - bmp::mpfr_float a_actual(Buf); - decn_to_str_complete(&BDecn); - CAPTURE(Buf); - bmp::mpfr_float b_actual(Buf); - //calc result - div_decn(); - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); // acc / b - - //calculate actual result - a_actual /= b_actual; - if (decn_is_nan(&AccDecn)){ - //check that NaN result of division by 0 - CAPTURE(a_actual); - CHECK(b_actual == 0); - } else { - bmp::mpfr_float calculated(Buf); - bmp::mpfr_float rel_diff = abs((a_actual - calculated) / a_actual); - CHECK(rel_diff < 2e-17); - } -} - -static void div_test( - //input - const char* a_str, int a_exp, - const char* b_str, int b_exp -) -{ - CAPTURE(a_str); CAPTURE(a_exp); - CAPTURE(b_str); CAPTURE(b_exp); - //do division - build_dec80(a_str, a_exp); - build_decn_at(&BDecn, b_str, b_exp); - div_test(); -} - -TEST_CASE("division"){ - div_test( - "1", 0, - "0", 0 - ); - - div_test( - "3.14", 60, - "-1.5", -2 - ); - - div_test( - "4", 0, - "4", 0 - ); - - div_test( - "1", 0, - "3", 0 - ); - - div_test( - "500", 0, - "99", 0 - ); - - div_test( - "500", 0, - "2", 0 - ); - - div_test( - "3", 0, - "25", -15 - ); - - div_test( - "0.02", 0, - "0.03", 0 - ); -} - -TEST_CASE("division random"){ - std::default_random_engine gen; - std::uniform_int_distribution distrib(0, 99); - std::uniform_int_distribution sign_distrib(0,1); - for (int j = 0; j < NUM_RAND_TESTS; j++){ - AccDecn.lsu[0] = distrib(gen); - BDecn.lsu[0] = distrib(gen); - for (int i = 1; i < DEC80_NUM_LSU; i++){ - AccDecn.lsu[i] = distrib(gen); - BDecn.lsu[i] = distrib(gen); - } - set_exponent(&AccDecn, distrib(gen), sign_distrib(gen)); - set_exponent(&BDecn, distrib(gen), sign_distrib(gen)); - div_test(); - } -} - -static void sqrt_test(){ - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); - //calculate result - sqrt_decn(); - //build mpfr float - bmp::mpfr_float::default_precision(50); - bmp::mpfr_float x_actual(Buf); - //print calc result - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); - //calculate actual result - CAPTURE(x_actual); - if (decn_is_nan(&AccDecn)){ - //check that NaN is from result of sqrt(-) - CHECK(x_actual <= 0); - } else if (decn_is_zero(&AccDecn)){ - //check actual is also 0 - CHECK(x_actual == 0); - } else { - x_actual = sqrt(x_actual); - CAPTURE(x_actual); - bmp::mpfr_float calculated(Buf); - bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual); - CHECK(rel_diff < 2e-17); - } -} - -static void sqrt_test(const char* x_str, int x_exp) -{ - CAPTURE(x_str); CAPTURE(x_exp); - build_dec80(x_str, x_exp); - sqrt_test(); -} - -TEST_CASE("sqrt"){ - sqrt_test("0", 0); - sqrt_test("2", 0); - sqrt_test("-1", 0); - sqrt_test("0.155", 0); - sqrt_test("10", 0); - sqrt_test("1.1", 10); - sqrt_test("2.02", -10); - sqrt_test("2.02", 0); - sqrt_test("1.5", 0); - sqrt_test("9", 99); - sqrt_test("123", 12345); -} - -TEST_CASE("sqrt random"){ - std::default_random_engine generator; - std::uniform_int_distribution distribution(0,99); - std::uniform_int_distribution exp_distrib(-99,99); - std::uniform_int_distribution sign_distrib(0,1); - for (int j = 0; j < NUM_RAND_TESTS; j++){ - for (int i = 0; i < DEC80_NUM_LSU; i++){ - AccDecn.lsu[i] = distribution(generator); - } - int sign = sign_distrib(generator); - set_exponent(&AccDecn, exp_distrib(generator), sign); - sqrt_test(); - } -} - -static void log_test_(bool base10, double epsilon){ - bmp::mpfr_float::default_precision(50); - CAPTURE(base10); - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); - //build mpfr float - bmp::mpfr_float x_actual(Buf); - //calculate result - if (base10){ - log10_decn(); - } else { - ln_decn(); - } - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); // log(x) - - //calculate actual result - CAPTURE(x_actual); - if (decn_is_nan(&AccDecn)){ - //check that NaN is from result of log(-) - CHECK(x_actual <= 0); - } else { - if (base10){ - x_actual = log10(x_actual); - } else { - x_actual = log(x_actual); - } - bmp::mpfr_float calculated(Buf); - CAPTURE(calculated); - bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual); - CHECK(rel_diff < epsilon); - } -} - -static void log_test(bool base10=false){ - //check if near 1.0 - remove_leading_zeros(&AccDecn); - double lsu0 = AccDecn.lsu[0]; - int exp = get_exponent(&AccDecn); - if (exp == -1){ - lsu0 /= (double) 10; - lsu0 += (double) AccDecn.lsu[1] / (10*100); - lsu0 += (double) AccDecn.lsu[2] / (10*100*100); - lsu0 += (double) AccDecn.lsu[3] / (10*100*100*100); - } else if (exp == 0){ - lsu0 += (double) AccDecn.lsu[1] / 100; - lsu0 += (double) AccDecn.lsu[2] / (100*100); - lsu0 += (double) AccDecn.lsu[3] / (100*100*100); - } - CAPTURE((int) AccDecn.lsu[0]); CAPTURE((int) AccDecn.lsu[1]); - CAPTURE(exp); - CAPTURE(lsu0); - if (exp == 0 || exp == -1){ - //check if near 1.0 - if (lsu0 >= 7 && lsu0 < 8){ - log_test_(base10, 7.5e-16); - } else if (lsu0 >= 8 && lsu0 < 9){ - log_test_(base10, 1.5e-15); - } else if (lsu0 >= 9 && lsu0 < 9.6){ - log_test_(base10, 1.0e-14); - } else if (lsu0 >= 9.6 && lsu0 < 9.9){ - log_test_(base10, 4.1e-13); - } else if (lsu0 >= 9.9 && lsu0 < 9.999){ - log_test_(base10, 1.5e-11); - } else if (lsu0 >= 9.999 && lsu0 < 9.99999){ - log_test_(base10, 6.0e-10); - } else if (lsu0 >= 9.99999 && lsu0 < 9.9999999){ - log_test_(base10, 3.0e-9); - } else if (lsu0 >= 9.9999999 && lsu0 < 10.0){ - log_test_(base10, 1.3e-7); - } else if (lsu0 >= 10.0 && lsu0 < 10.00001){ - log_test_(base10, 6.0e-10); - } else if (lsu0 >= 10.00001 && lsu0 < 10.001){ - log_test_(base10, 6.0e-11); - } else if (lsu0 >= 10.001 && lsu0 < 10.1){ - log_test_(base10, 1.5e-12); - } else if (lsu0 >= 10.1 && lsu0 < 11){ - log_test_(base10, 1.6e-14); - } else if (lsu0 >= 11 && lsu0 < 13){ - log_test_(base10, 2.0e-15); - } else { - log_test_(base10, 6.5e-16); - } - } else { - log_test_(base10, 2e-16); - } -} - -static void log_test( - //input - const char* x_str, int x_exp, - bool base10=false -) -{ - CAPTURE(x_str); CAPTURE(x_exp); - CAPTURE(base10); - build_dec80(x_str, x_exp); - log_test(base10); -} - -TEST_CASE("log"){ - log_test("0", 0); - log_test("-1", 0); - log_test("0.155", 0); - log_test("10", 0); - log_test("1.1", 10); - log_test("2.02", -10); - log_test("2.02", 0); - log_test("1.5", 0, true); - log_test("9", 99); - log_test("123", 12345); -} - -TEST_CASE("log random"){ - std::default_random_engine gen; - std::uniform_int_distribution distrib(0,99); - std::uniform_int_distribution exp_distrib(-99,99); - std::uniform_int_distribution sign_distrib(0,1); - for (int j = 0; j < NUM_RAND_TESTS; j++){ - for (int i = 0; i < DEC80_NUM_LSU; i++){ - AccDecn.lsu[i] = distrib(gen); - } - int exp = exp_distrib(gen); - set_exponent(&AccDecn, exp, 0); - int base10 = sign_distrib(gen); - log_test(base10); - } -} - -static void log_test_near1(int lsu0_low, int lsu0_high, int exp){ - std::default_random_engine gen; - std::uniform_int_distribution lsu0_distrib(lsu0_low, lsu0_high); - std::uniform_int_distribution distrib(0,99); - std::uniform_int_distribution exp_distrib(-99,99); - std::uniform_int_distribution sign_distrib(0,1); - for (int j = 0; j < NUM_RAND_TESTS; j++){ - AccDecn.lsu[0] = lsu0_distrib(gen); - for (int i = 1; i < DEC80_NUM_LSU; i++){ - AccDecn.lsu[i] = distrib(gen); - } - set_exponent(&AccDecn, exp, 0); - int base10 = sign_distrib(gen); - log_test(base10); - } -} - -TEST_CASE("log random 0 to 0.99..."){ - log_test_near1(0, 99, -1); -} -TEST_CASE("log random 0.8 to 0.99..."){ - log_test_near1(80, 99, -1); -} -TEST_CASE("log random 1.0 to 9.9"){ - log_test_near1(10, 99, 0); -} -TEST_CASE("log random 1.0 to 2.0"){ - log_test_near1(10, 20, 0); -} - -static void exp_test_(bool base10, double epsilon){ - bmp::mpfr_float::default_precision(50); - CAPTURE(base10); - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); //x - CAPTURE(AccDecn.exponent); - //build mpfr float - bmp::mpfr_float x_actual(Buf); - //calculate result - if (base10){ - exp10_decn(); - } else { - exp_decn(); - } - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); // exp(x) - - //calculate actual result - bmp::mpfr_float calculated(Buf); - if (base10){ - x_actual *= log(10); - } - x_actual = exp(x_actual); - CAPTURE(x_actual); - bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual); - CHECK(rel_diff < epsilon); -} - -static void exp_test(bool base10=false){ - double x; - int exp = get_exponent(&AccDecn); - if (exp == 1){ - x = AccDecn.lsu[0]; - x += (double) AccDecn.lsu[1] / 100; - } else if (exp == 2){ - x = (double) AccDecn.lsu[0] * 10; - x += (double) AccDecn.lsu[1] / 10; - } - CAPTURE((int) AccDecn.lsu[0]); CAPTURE((int) AccDecn.lsu[1]); - CAPTURE(exp); - CAPTURE(x); - double epsilon; - if (exp == 1 || exp == 2){ - if (x > 230){ - epsilon = 8e-15; - } else if (x > 210){ - epsilon = 6e-15; - } else if (x > 180){ - epsilon = 5e-15; - } else if (x > 150){ - epsilon = 4e-15; - } else if (x > 125){ - epsilon = 3e-15; - } else if (x > 100){ - epsilon = 2e-15; - } else if (x > 65){ - epsilon = 1e-15; - } - } else { - epsilon = 6e-16; - } - CAPTURE(base10); - if (base10){ - epsilon *= 20; - } - exp_test_(base10, epsilon); -} - -static void exp_test( - //input - const char* x_str, int x_exp, - bool base10=false -) -{ - CAPTURE(x_str); CAPTURE(x_exp); - CAPTURE(base10); - build_dec80(x_str, x_exp); - exp_test(base10); -} - -static void exp10_test(const char* x_str, int x_exp){ - exp_test(x_str, x_exp, true); -} - -TEST_CASE("exp"){ - exp_test("4.4", 0); - exp_test("0.155", 0); - exp_test("9.999", 0); - exp_test("10", 0); - exp_test("10.001", 0); - exp_test("2.3", 2);//, 6e-15); - exp_test("2.02", -10); - exp_test("2.02", 0); - exp_test("1.5", 0); - exp_test("99.999999", 0); - exp_test("230.2", 0);//, 6e-15); - exp_test("-230", 0);//, 6e-15); - exp_test("294.69999999", 0);//, 8e-15); - - //do not operate on NaN - set_dec80_NaN(&AccDecn); - exp_decn(); - CHECK(decn_is_nan(&AccDecn)); //still NaN -} - - -TEST_CASE("exp10"){ - exp10_test("4.4", 0); - exp10_test("0.155", 0); - exp10_test("9.999", 0); - exp10_test("10", 0); - exp10_test("10.001", 0); - exp10_test("2.02", -10); - exp10_test("2.02", 0); - exp10_test("1.5", 0); - exp10_test("127", 0);//, 3e-14); - exp10_test("99.999999", 0);//, 2e-14); -} - -static void test_exp_random(int exp_distrib_low){ - std::default_random_engine gen; - std::uniform_int_distribution distrib(0, 99); - std::uniform_int_distribution lsu0_high_distrib(0, 23); - std::uniform_int_distribution exp_distrib(exp_distrib_low, 2); - std::uniform_int_distribution sign_distrib(0, 1); - for (int j = 0; j < NUM_RAND_TESTS; j++){ - int exp = exp_distrib(gen); - int sign = sign_distrib(gen); - if (exp == 2) { - //limit x to approximately +/- 230 - AccDecn.lsu[0] = lsu0_high_distrib(gen); - } else { - AccDecn.lsu[0] = distrib(gen); - } - for (int i = 1; i < DEC80_NUM_LSU; i++){ - AccDecn.lsu[i] = distrib(gen); - } - set_exponent(&AccDecn, exp, sign); - exp_test(); - } -} - -TEST_CASE("exp random"){ - test_exp_random(-99); -} -TEST_CASE("exp large random"){ - test_exp_random(1); -} - -static void pow_test(){ // a^b - bmp::mpf_float::default_precision(50); - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); // a - bmp::mpfr_float a_actual(Buf); - decn_to_str_complete(&BDecn); - CAPTURE(Buf); // b - bmp::mpfr_float b_actual(Buf); - //calculate result - pow_decn(); - //calculate actual result - bmp::mpfr_float res_actual(pow(a_actual, b_actual)); - //check overflow or underflow - if (decn_is_nan(&AccDecn)){ - //check overflow or underflow - if (b_actual > 0) { - CHECK(log(res_actual) > 100); - } else { - CHECK(log(res_actual) < -100); - } - return; - } - //not over/underflow, get string and log calculated result - decn_to_str_complete(&AccDecn); - CAPTURE(Buf); // a^b - bmp::mpfr_float calculated(Buf); - //check relative error - double rel_tol = 4.5e-14; - if (a_actual > 1.0 && a_actual < 1.0001){ - rel_tol = 1e-7; - } else if (a_actual > 0.9 && a_actual < 2.0){ - rel_tol = 1.5e-10; - } else if (log(res_actual) > 100){ - rel_tol = 1e-12; - } - CAPTURE(a_actual); - CAPTURE(rel_tol); - if (decn_is_zero(&AccDecn)) { - bmp::mpfr_float diff = abs(res_actual - calculated); - CHECK(diff < rel_tol); - } else { - bmp::mpfr_float rel_diff = abs((res_actual - calculated)/res_actual); - CHECK(rel_diff < rel_tol); - } -} - -static void pow_test( - //input - const char* a_str, int a_exp, - const char* b_str, int b_exp -) -{ - CAPTURE(a_str); CAPTURE(a_exp); - CAPTURE(b_str); CAPTURE(b_exp); - - //compute power - build_decn_at(&BDecn, b_str, b_exp); - build_dec80(a_str, a_exp); - pow_test(); -} - -TEST_CASE("power"){ - pow_test( - "3.14", 60, - "-1.5", -2 - ); - - pow_test( - "3", 0, - "201", 0 - ); - - pow_test( - "5", 0, - "0", 0 - ); - - pow_test( - "5", 0, - "0", 2 - ); - - pow_test( - "0", 0, - "5", 0 - ); - - pow_test( - "0", 0, - "0", 0 - ); -} - -static void power_test(int lsu0_low, int lsu0_high, int exp_low=-99, int exp_high=99){ - std::default_random_engine gen; - std::uniform_int_distribution lsu0_distrib(lsu0_low, lsu0_high); - std::uniform_int_distribution distrib(0, 99); - std::uniform_int_distribution exp_distrib(exp_low, exp_high); - std::uniform_int_distribution sign_distrib(0,1); - for (int j = 0; j < NUM_RAND_TESTS; j++){ - AccDecn.lsu[0] = lsu0_distrib(gen); - for (int i = 1; i < DEC80_NUM_LSU; i++){ - AccDecn.lsu[i] = distrib(gen); - BDecn.lsu[i] = distrib(gen); - } - set_exponent(&AccDecn, exp_distrib(gen), 0); - //generate exponent for b to minimize chance of a^b overflowing: - // a^b <= 1e100 - // b*log(a) <= log(1e100) = 100 - // b <= 100/log(a) - // b_exponent <= log(100/log(a)) = log(100) - log(log(a)) - // b_exponent <= 2 - log(log(a)) - decn_to_str_complete(&AccDecn); - bmp::mpfr_float acc(Buf); - acc = 2.0 - log(log(acc)); - double b_exponent_high_flt = acc.convert_to(); - int b_exponent_high = b_exponent_high_flt; - int b_exponent_low = -99; - //ensure b_exponent high in range - if (b_exponent_high > 99){ - b_exponent_high = 99; - } else if (b_exponent_high < b_exponent_low){ - b_exponent_high = b_exponent_low; - } - CAPTURE(b_exponent_low); - CAPTURE(b_exponent_high); - std::uniform_int_distribution b_exp_distrib(b_exponent_low, b_exponent_high); - int b_exponent = b_exp_distrib(gen); - CAPTURE(b_exponent); - int b_neg = sign_distrib(gen); - set_exponent(&BDecn, b_exponent, b_neg); - pow_test(); - } -} - -TEST_CASE("power random"){ - power_test(0, 99); -} -TEST_CASE("power random 0.9 to 0.99..."){ - power_test(90, 99, -1, -1); -} -TEST_CASE("power random 1.0 to 2.0..."){ - power_test(10, 20, 0, 0); -} - TEST_CASE("u32str corner"){ u32str(0, &Buf[0], 10); CHECK_THAT(Buf, Equals("0")); diff --git a/src/decn/decn_tests.h b/src/decn/decn_tests.h new file mode 100644 index 0000000..15ecdf6 --- /dev/null +++ b/src/decn/decn_tests.h @@ -0,0 +1,28 @@ +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* + * decn_tests.h + * + * Created on: Oct 26, 2020 + */ + + +#ifndef DECN_TESTS_H_ +#define DECN_TESTS_H_ + + +static const int NUM_RAND_TESTS = 123456; + + +#endif diff --git a/src/decn/decn_tests_div_sqrt.cpp b/src/decn/decn_tests_div_sqrt.cpp new file mode 100644 index 0000000..ccc874d --- /dev/null +++ b/src/decn/decn_tests_div_sqrt.cpp @@ -0,0 +1,200 @@ +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* + * decn_tests_div_sqrt.cpp + * + * Unit tests using https://github.com/catchorg/Catch2 + * + * separate out reciprocal/division and sqrt tests + * + * Created on: Oct 26, 2020 + */ + + +#include +#include +#include +#include +#include "decn.h" +#include "../utils.h" + +#include "decn_tests.h" + + +namespace bmp = boost::multiprecision; +using Catch::Matchers::Equals; + + +static void div_test(){ //acc / b + bmp::mpf_float::default_precision(50); + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); + bmp::mpfr_float a_actual(Buf); + decn_to_str_complete(&BDecn); + CAPTURE(Buf); + bmp::mpfr_float b_actual(Buf); + //calc result + div_decn(); + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); // acc / b + + //calculate actual result + a_actual /= b_actual; + if (decn_is_nan(&AccDecn)){ + //check that NaN result of division by 0 + CAPTURE(a_actual); + CHECK(b_actual == 0); + } else { + bmp::mpfr_float calculated(Buf); + bmp::mpfr_float rel_diff = abs((a_actual - calculated) / a_actual); + CHECK(rel_diff < 2e-17); + } +} + +static void div_test( + //input + const char* a_str, int a_exp, + const char* b_str, int b_exp +) +{ + CAPTURE(a_str); CAPTURE(a_exp); + CAPTURE(b_str); CAPTURE(b_exp); + //do division + build_dec80(a_str, a_exp); + build_decn_at(&BDecn, b_str, b_exp); + div_test(); +} + +TEST_CASE("division"){ + div_test( + "1", 0, + "0", 0 + ); + + div_test( + "3.14", 60, + "-1.5", -2 + ); + + div_test( + "4", 0, + "4", 0 + ); + + div_test( + "1", 0, + "3", 0 + ); + + div_test( + "500", 0, + "99", 0 + ); + + div_test( + "500", 0, + "2", 0 + ); + + div_test( + "3", 0, + "25", -15 + ); + + div_test( + "0.02", 0, + "0.03", 0 + ); +} + +TEST_CASE("division random"){ + std::default_random_engine gen; + std::uniform_int_distribution distrib(0, 99); + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < NUM_RAND_TESTS; j++){ + AccDecn.lsu[0] = distrib(gen); + BDecn.lsu[0] = distrib(gen); + for (int i = 1; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + BDecn.lsu[i] = distrib(gen); + } + set_exponent(&AccDecn, distrib(gen), sign_distrib(gen)); + set_exponent(&BDecn, distrib(gen), sign_distrib(gen)); + div_test(); + } +} + +static void sqrt_test(){ + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); + //calculate result + sqrt_decn(); + //build mpfr float + bmp::mpfr_float::default_precision(50); + bmp::mpfr_float x_actual(Buf); + //print calc result + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); + //calculate actual result + CAPTURE(x_actual); + if (decn_is_nan(&AccDecn)){ + //check that NaN is from result of sqrt(-) + CHECK(x_actual <= 0); + } else if (decn_is_zero(&AccDecn)){ + //check actual is also 0 + CHECK(x_actual == 0); + } else { + x_actual = sqrt(x_actual); + CAPTURE(x_actual); + bmp::mpfr_float calculated(Buf); + bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual); + CHECK(rel_diff < 2e-17); + } +} + +static void sqrt_test(const char* x_str, int x_exp) +{ + CAPTURE(x_str); CAPTURE(x_exp); + build_dec80(x_str, x_exp); + sqrt_test(); +} + +TEST_CASE("sqrt"){ + sqrt_test("0", 0); + sqrt_test("2", 0); + sqrt_test("-1", 0); + sqrt_test("0.155", 0); + sqrt_test("10", 0); + sqrt_test("1.1", 10); + sqrt_test("2.02", -10); + sqrt_test("2.02", 0); + sqrt_test("1.5", 0); + sqrt_test("9", 99); + sqrt_test("123", 12345); +} + +TEST_CASE("sqrt random"){ + std::default_random_engine generator; + std::uniform_int_distribution distribution(0,99); + std::uniform_int_distribution exp_distrib(-99,99); + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < NUM_RAND_TESTS; j++){ + for (int i = 0; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distribution(generator); + } + int sign = sign_distrib(generator); + set_exponent(&AccDecn, exp_distrib(generator), sign); + sqrt_test(); + } +} diff --git a/src/decn/decn_tests_transcendental.cpp b/src/decn/decn_tests_transcendental.cpp new file mode 100644 index 0000000..2d70631 --- /dev/null +++ b/src/decn/decn_tests_transcendental.cpp @@ -0,0 +1,490 @@ +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* + * decn_tests_transcendental.cpp + * + * Unit tests using https://github.com/catchorg/Catch2 + * + * separate out transcendental function tests + * + * Created on: Oct 26, 2020 + */ + + +#include +#include +#include +#include +#include "decn.h" +#include "../utils.h" + +#include "decn_tests.h" + + +namespace bmp = boost::multiprecision; +using Catch::Matchers::Equals; + + +static void log_test_(bool base10, double epsilon){ + bmp::mpfr_float::default_precision(50); + CAPTURE(base10); + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); + //build mpfr float + bmp::mpfr_float x_actual(Buf); + //calculate result + if (base10){ + log10_decn(); + } else { + ln_decn(); + } + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); // log(x) + + //calculate actual result + CAPTURE(x_actual); + if (decn_is_nan(&AccDecn)){ + //check that NaN is from result of log(-) + CHECK(x_actual <= 0); + } else { + if (base10){ + x_actual = log10(x_actual); + } else { + x_actual = log(x_actual); + } + bmp::mpfr_float calculated(Buf); + CAPTURE(calculated); + bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual); + CHECK(rel_diff < epsilon); + } +} + +static void log_test(bool base10=false){ + //check if near 1.0 + remove_leading_zeros(&AccDecn); + double lsu0 = AccDecn.lsu[0]; + int exp = get_exponent(&AccDecn); + if (exp == -1){ + lsu0 /= (double) 10; + lsu0 += (double) AccDecn.lsu[1] / (10*100); + lsu0 += (double) AccDecn.lsu[2] / (10*100*100); + lsu0 += (double) AccDecn.lsu[3] / (10*100*100*100); + } else if (exp == 0){ + lsu0 += (double) AccDecn.lsu[1] / 100; + lsu0 += (double) AccDecn.lsu[2] / (100*100); + lsu0 += (double) AccDecn.lsu[3] / (100*100*100); + } + CAPTURE((int) AccDecn.lsu[0]); CAPTURE((int) AccDecn.lsu[1]); + CAPTURE(exp); + CAPTURE(lsu0); + if (exp == 0 || exp == -1){ + //check if near 1.0 + if (lsu0 >= 7 && lsu0 < 8){ + log_test_(base10, 7.5e-16); + } else if (lsu0 >= 8 && lsu0 < 9){ + log_test_(base10, 1.5e-15); + } else if (lsu0 >= 9 && lsu0 < 9.6){ + log_test_(base10, 1.0e-14); + } else if (lsu0 >= 9.6 && lsu0 < 9.9){ + log_test_(base10, 4.1e-13); + } else if (lsu0 >= 9.9 && lsu0 < 9.999){ + log_test_(base10, 1.5e-11); + } else if (lsu0 >= 9.999 && lsu0 < 9.99999){ + log_test_(base10, 6.0e-10); + } else if (lsu0 >= 9.99999 && lsu0 < 9.9999999){ + log_test_(base10, 3.0e-9); + } else if (lsu0 >= 9.9999999 && lsu0 < 10.0){ + log_test_(base10, 1.3e-7); + } else if (lsu0 >= 10.0 && lsu0 < 10.00001){ + log_test_(base10, 6.0e-10); + } else if (lsu0 >= 10.00001 && lsu0 < 10.001){ + log_test_(base10, 6.0e-11); + } else if (lsu0 >= 10.001 && lsu0 < 10.1){ + log_test_(base10, 1.5e-12); + } else if (lsu0 >= 10.1 && lsu0 < 11){ + log_test_(base10, 1.6e-14); + } else if (lsu0 >= 11 && lsu0 < 13){ + log_test_(base10, 2.0e-15); + } else { + log_test_(base10, 6.5e-16); + } + } else { + log_test_(base10, 2e-16); + } +} + +static void log_test( + //input + const char* x_str, int x_exp, + bool base10=false +) +{ + CAPTURE(x_str); CAPTURE(x_exp); + CAPTURE(base10); + build_dec80(x_str, x_exp); + log_test(base10); +} + +TEST_CASE("log"){ + log_test("0", 0); + log_test("-1", 0); + log_test("0.155", 0); + log_test("10", 0); + log_test("1.1", 10); + log_test("2.02", -10); + log_test("2.02", 0); + log_test("1.5", 0, true); + log_test("9", 99); + log_test("123", 12345); +} + +TEST_CASE("log random"){ + std::default_random_engine gen; + std::uniform_int_distribution distrib(0,99); + std::uniform_int_distribution exp_distrib(-99,99); + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < NUM_RAND_TESTS; j++){ + for (int i = 0; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + } + int exp = exp_distrib(gen); + set_exponent(&AccDecn, exp, 0); + int base10 = sign_distrib(gen); + log_test(base10); + } +} + +static void log_test_near1(int lsu0_low, int lsu0_high, int exp){ + std::default_random_engine gen; + std::uniform_int_distribution lsu0_distrib(lsu0_low, lsu0_high); + std::uniform_int_distribution distrib(0,99); + std::uniform_int_distribution exp_distrib(-99,99); + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < NUM_RAND_TESTS; j++){ + AccDecn.lsu[0] = lsu0_distrib(gen); + for (int i = 1; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + } + set_exponent(&AccDecn, exp, 0); + int base10 = sign_distrib(gen); + log_test(base10); + } +} + +TEST_CASE("log random 0 to 0.99..."){ + log_test_near1(0, 99, -1); +} +TEST_CASE("log random 0.8 to 0.99..."){ + log_test_near1(80, 99, -1); +} +TEST_CASE("log random 1.0 to 9.9"){ + log_test_near1(10, 99, 0); +} +TEST_CASE("log random 1.0 to 2.0"){ + log_test_near1(10, 20, 0); +} + +static void exp_test_(bool base10, double epsilon){ + bmp::mpfr_float::default_precision(50); + CAPTURE(base10); + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); //x + CAPTURE(AccDecn.exponent); + //build mpfr float + bmp::mpfr_float x_actual(Buf); + //calculate result + if (base10){ + exp10_decn(); + } else { + exp_decn(); + } + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); // exp(x) + + //calculate actual result + bmp::mpfr_float calculated(Buf); + if (base10){ + x_actual *= log(10); + } + x_actual = exp(x_actual); + CAPTURE(x_actual); + bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual); + CHECK(rel_diff < epsilon); +} + +static void exp_test(bool base10=false){ + double x; + int exp = get_exponent(&AccDecn); + if (exp == 1){ + x = AccDecn.lsu[0]; + x += (double) AccDecn.lsu[1] / 100; + } else if (exp == 2){ + x = (double) AccDecn.lsu[0] * 10; + x += (double) AccDecn.lsu[1] / 10; + } + CAPTURE((int) AccDecn.lsu[0]); CAPTURE((int) AccDecn.lsu[1]); + CAPTURE(exp); + CAPTURE(x); + double epsilon; + if (exp == 1 || exp == 2){ + if (x > 230){ + epsilon = 8e-15; + } else if (x > 210){ + epsilon = 6e-15; + } else if (x > 180){ + epsilon = 5e-15; + } else if (x > 150){ + epsilon = 4e-15; + } else if (x > 125){ + epsilon = 3e-15; + } else if (x > 100){ + epsilon = 2e-15; + } else if (x > 65){ + epsilon = 1e-15; + } + } else { + epsilon = 6e-16; + } + CAPTURE(base10); + if (base10){ + epsilon *= 20; + } + exp_test_(base10, epsilon); +} + +static void exp_test( + //input + const char* x_str, int x_exp, + bool base10=false +) +{ + CAPTURE(x_str); CAPTURE(x_exp); + CAPTURE(base10); + build_dec80(x_str, x_exp); + exp_test(base10); +} + +static void exp10_test(const char* x_str, int x_exp){ + exp_test(x_str, x_exp, true); +} + +TEST_CASE("exp"){ + exp_test("4.4", 0); + exp_test("0.155", 0); + exp_test("9.999", 0); + exp_test("10", 0); + exp_test("10.001", 0); + exp_test("2.3", 2);//, 6e-15); + exp_test("2.02", -10); + exp_test("2.02", 0); + exp_test("1.5", 0); + exp_test("99.999999", 0); + exp_test("230.2", 0);//, 6e-15); + exp_test("-230", 0);//, 6e-15); + exp_test("294.69999999", 0);//, 8e-15); + + //do not operate on NaN + set_dec80_NaN(&AccDecn); + exp_decn(); + CHECK(decn_is_nan(&AccDecn)); //still NaN +} + + +TEST_CASE("exp10"){ + exp10_test("4.4", 0); + exp10_test("0.155", 0); + exp10_test("9.999", 0); + exp10_test("10", 0); + exp10_test("10.001", 0); + exp10_test("2.02", -10); + exp10_test("2.02", 0); + exp10_test("1.5", 0); + exp10_test("127", 0);//, 3e-14); + exp10_test("99.999999", 0);//, 2e-14); +} + +static void test_exp_random(int exp_distrib_low){ + std::default_random_engine gen; + std::uniform_int_distribution distrib(0, 99); + std::uniform_int_distribution lsu0_high_distrib(0, 23); + std::uniform_int_distribution exp_distrib(exp_distrib_low, 2); + std::uniform_int_distribution sign_distrib(0, 1); + for (int j = 0; j < NUM_RAND_TESTS; j++){ + int exp = exp_distrib(gen); + int sign = sign_distrib(gen); + if (exp == 2) { + //limit x to approximately +/- 230 + AccDecn.lsu[0] = lsu0_high_distrib(gen); + } else { + AccDecn.lsu[0] = distrib(gen); + } + for (int i = 1; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + } + set_exponent(&AccDecn, exp, sign); + exp_test(); + } +} + +TEST_CASE("exp random"){ + test_exp_random(-99); +} +TEST_CASE("exp large random"){ + test_exp_random(1); +} + +static void pow_test(){ // a^b + bmp::mpf_float::default_precision(50); + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); // a + bmp::mpfr_float a_actual(Buf); + decn_to_str_complete(&BDecn); + CAPTURE(Buf); // b + bmp::mpfr_float b_actual(Buf); + //calculate result + pow_decn(); + //calculate actual result + bmp::mpfr_float res_actual(pow(a_actual, b_actual)); + //check overflow or underflow + if (decn_is_nan(&AccDecn)){ + //check overflow or underflow + if (b_actual > 0) { + CHECK(log(res_actual) > 100); + } else { + CHECK(log(res_actual) < -100); + } + return; + } + //not over/underflow, get string and log calculated result + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); // a^b + bmp::mpfr_float calculated(Buf); + //check relative error + double rel_tol = 4.5e-14; + if (a_actual > 1.0 && a_actual < 1.0001){ + rel_tol = 1e-7; + } else if (a_actual > 0.9 && a_actual < 2.0){ + rel_tol = 1.5e-10; + } else if (log(res_actual) > 100){ + rel_tol = 1e-12; + } + CAPTURE(a_actual); + CAPTURE(rel_tol); + if (decn_is_zero(&AccDecn)) { + bmp::mpfr_float diff = abs(res_actual - calculated); + CHECK(diff < rel_tol); + } else { + bmp::mpfr_float rel_diff = abs((res_actual - calculated)/res_actual); + CHECK(rel_diff < rel_tol); + } +} + +static void pow_test( + //input + const char* a_str, int a_exp, + const char* b_str, int b_exp +) +{ + CAPTURE(a_str); CAPTURE(a_exp); + CAPTURE(b_str); CAPTURE(b_exp); + + //compute power + build_decn_at(&BDecn, b_str, b_exp); + build_dec80(a_str, a_exp); + pow_test(); +} + +TEST_CASE("power"){ + pow_test( + "3.14", 60, + "-1.5", -2 + ); + + pow_test( + "3", 0, + "201", 0 + ); + + pow_test( + "5", 0, + "0", 0 + ); + + pow_test( + "5", 0, + "0", 2 + ); + + pow_test( + "0", 0, + "5", 0 + ); + + pow_test( + "0", 0, + "0", 0 + ); +} + +static void power_test(int lsu0_low, int lsu0_high, int exp_low=-99, int exp_high=99){ + std::default_random_engine gen; + std::uniform_int_distribution lsu0_distrib(lsu0_low, lsu0_high); + std::uniform_int_distribution distrib(0, 99); + std::uniform_int_distribution exp_distrib(exp_low, exp_high); + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < NUM_RAND_TESTS; j++){ + AccDecn.lsu[0] = lsu0_distrib(gen); + for (int i = 1; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + BDecn.lsu[i] = distrib(gen); + } + set_exponent(&AccDecn, exp_distrib(gen), 0); + //generate exponent for b to minimize chance of a^b overflowing: + // a^b <= 1e100 + // b*log(a) <= log(1e100) = 100 + // b <= 100/log(a) + // b_exponent <= log(100/log(a)) = log(100) - log(log(a)) + // b_exponent <= 2 - log(log(a)) + decn_to_str_complete(&AccDecn); + bmp::mpfr_float acc(Buf); + acc = 2.0 - log(log(acc)); + double b_exponent_high_flt = acc.convert_to(); + int b_exponent_high = b_exponent_high_flt; + int b_exponent_low = -99; + //ensure b_exponent high in range + if (b_exponent_high > 99){ + b_exponent_high = 99; + } else if (b_exponent_high < b_exponent_low){ + b_exponent_high = b_exponent_low; + } + CAPTURE(b_exponent_low); + CAPTURE(b_exponent_high); + std::uniform_int_distribution b_exp_distrib(b_exponent_low, b_exponent_high); + int b_exponent = b_exp_distrib(gen); + CAPTURE(b_exponent); + int b_neg = sign_distrib(gen); + set_exponent(&BDecn, b_exponent, b_neg); + pow_test(); + } +} + +TEST_CASE("power random"){ + power_test(0, 99); +} +TEST_CASE("power random 0.9 to 0.99..."){ + power_test(90, 99, -1, -1); +} +TEST_CASE("power random 1.0 to 2.0..."){ + power_test(10, 20, 0, 0); +} From f15790f252077efe16bfa05d2e23a39209570647 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Thu, 10 Dec 2020 22:54:45 -0500 Subject: [PATCH 09/10] add random sin/cos/tan trig tests (accuracy is fairly bad) --- src/decn/decn_tests_trig.cpp | 244 ++++++++++++++++++++++++++++++----- 1 file changed, 210 insertions(+), 34 deletions(-) diff --git a/src/decn/decn_tests_trig.cpp b/src/decn/decn_tests_trig.cpp index c8b53a6..3217287 100644 --- a/src/decn/decn_tests_trig.cpp +++ b/src/decn/decn_tests_trig.cpp @@ -1,26 +1,41 @@ +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + #include #include #include #include "decn.h" + +#include "decn_tests.h" + namespace bmp = boost::multiprecision; using Catch::Matchers::Equals; static void trig_test(void (*operation)(void), bmp::mpfr_float (*mpfr_operation)(bmp::mpfr_float x), - const char* a_str, int a_exp, double rtol, double atol) + double rtol, double atol) { - CAPTURE(a_str); CAPTURE(a_exp); - build_dec80(a_str, a_exp); + //build mpfr float + bmp::mpfr_float::default_precision(50); + decn_to_str_complete(&AccDecn); + CAPTURE(Buf); + bmp::mpfr_float a_actual(Buf); + //calculate operation(); decn_to_str_complete(&AccDecn); CAPTURE(Buf); - - bmp::mpfr_float::default_precision(50); - std::string a_full_str(a_str); - a_full_str += "e" + std::to_string(a_exp); - - bmp::mpfr_float a_actual(a_full_str); + //calculate actual a_actual = mpfr_operation(a_actual); CAPTURE(a_actual); @@ -32,50 +47,92 @@ static void trig_test(void (*operation)(void), bmp::mpfr_float (*mpfr_operation) bmp::mpfr_float diff = abs(a_actual - calculated); CHECK(diff < atol); } - } -static void sin_test( - const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) + +static void sin_test(double rtol=5e-3, double atol=1e-3) { - trig_test(sin_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return sin(x);}, a_str, a_exp, rtol, atol); + CAPTURE("sin test"); + trig_test(sin_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return sin(x);}, rtol, atol); } -static void cos_test( - const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) +static void sin_test(const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) { - trig_test(cos_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return cos(x);}, a_str, a_exp, rtol, atol); + CAPTURE(a_str); CAPTURE(a_exp); + build_dec80(a_str, a_exp); + sin_test(rtol, atol); } -static void tan_test( - const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) + +static void cos_test(double rtol=5e-3, double atol=1e-3) { - trig_test(tan_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return tan(x);}, a_str, a_exp, rtol, atol); + CAPTURE("cos test"); + trig_test(cos_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return cos(x);}, rtol, atol); } -static void atan_test( - const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) +static void cos_test(const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) { - trig_test(arctan_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return atan(x);}, a_str, a_exp, rtol, atol); + CAPTURE(a_str); CAPTURE(a_exp); + build_dec80(a_str, a_exp); + cos_test(rtol, atol); } -static void asin_test( - const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) + +static void tan_test(double rtol=5e-3, double atol=1e-3) { - trig_test(arcsin_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return asin(x);}, a_str, a_exp, rtol, atol); + trig_test(tan_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return tan(x);}, rtol, atol); } -static void acos_test( - const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) +static void tan_test(const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) { - trig_test(arccos_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return acos(x);}, a_str, a_exp, rtol, atol); + CAPTURE(a_str); CAPTURE(a_exp); + build_dec80(a_str, a_exp); + tan_test(rtol, atol); } +static void atan_test(double rtol=5e-3, double atol=1e-3) +{ + trig_test(arctan_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return atan(x);}, rtol, atol); +} + +static void atan_test(const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) +{ + CAPTURE(a_str); CAPTURE(a_exp); + build_dec80(a_str, a_exp); + atan_test(rtol, atol); +} + + +static void asin_test(double rtol=5e-3, double atol=1e-3) +{ + trig_test(arcsin_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return asin(x);}, rtol, atol); +} + +static void asin_test(const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) +{ + CAPTURE(a_str); CAPTURE(a_exp); + build_dec80(a_str, a_exp); + asin_test(rtol, atol); +} + + +static void acos_test(double rtol=5e-3, double atol=1e-3) +{ + trig_test(arccos_decn, [](bmp::mpfr_float x) -> bmp::mpfr_float {return acos(x);}, rtol, atol); +} + +static void acos_test(const char* a_str, int a_exp, double rtol=5e-3, double atol=1e-3) +{ + CAPTURE(a_str); CAPTURE(a_exp); + build_dec80(a_str, a_exp); + acos_test(rtol, atol); +} + const char * const pi = "3.141592653589793239"; const char * const pi_threequarters = "2.356194490192344929"; -const char * const pi_halfed = "1.570796326794896619"; -const char * const pi_quarted = ".7853981633974483096"; +const char * const pi_halved = "1.570796326794896619"; +const char * const pi_quarter = ".7853981633974483096"; TEST_CASE("sin") { @@ -96,8 +153,8 @@ TEST_CASE("sin") { sin_test("2.5", 0); sin_test("3.0", 0); sin_test(pi, 0, -1); - sin_test(pi_quarted, 0); - sin_test(pi_halfed, 0); + sin_test(pi_quarter, 0); + sin_test(pi_halved, 0); sin_test(pi_threequarters, 0); sin_test("1000.0", 0); sin_test("-0.5", 0); @@ -129,8 +186,8 @@ TEST_CASE("cos") { cos_test("2.5", 0); cos_test("3.0", 0); cos_test(pi, 0); - cos_test(pi_quarted, 0); - cos_test(pi_halfed, 0, -1); + cos_test(pi_quarter, 0); + cos_test(pi_halved, 0, -1); cos_test(pi_threequarters, 0); cos_test("1000.0", 0); cos_test("-0.5", 0); @@ -201,3 +258,122 @@ TEST_CASE("arccos") { acos_test("0.9", 0); acos_test("-0.9", 0); } + + +static const int NUM_RAND_TRIG_TESTS = 4321; //trig tests are slow + +TEST_CASE("sin random"){ + std::default_random_engine gen; + std::uniform_int_distribution distrib(0,99); + std::uniform_int_distribution exp_distrib(-1,0); //restrict range for now + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < NUM_RAND_TRIG_TESTS; j++){ + for (int i = 0; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + } + int exp = exp_distrib(gen); + int sign = sign_distrib(gen); + set_exponent(&AccDecn, exp, sign); + int lsu0 = AccDecn.lsu[0]; + CAPTURE(lsu0); + CAPTURE(exp); + CAPTURE(sign); + if (exp == -1 && lsu0 == 0){ + //very small + sin_test(40); + } else if ((exp == -1 && lsu0 < 10) || (exp == 0 && lsu0 == 0)){ + //small + sin_test(0.4); + } else if ((exp == 0 && lsu0 == 31)){ + //near pi + sin_test(0.2); + } else if ((exp == 0 && lsu0 == 62)){ + //near 2pi + sin_test(0.2); + } else if ((exp == 0 && lsu0 > 62)){ + //large + sin_test(0.1); + } else { + sin_test(0.02); + } + } +} + +TEST_CASE("cos random"){ + std::default_random_engine gen; + std::uniform_int_distribution distrib(0,99); + std::uniform_int_distribution exp_distrib(-1,0); //restrict range for now + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < NUM_RAND_TRIG_TESTS; j++){ + for (int i = 0; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + } + int exp = exp_distrib(gen); + int sign = sign_distrib(gen); + set_exponent(&AccDecn, exp, sign); + int lsu0 = AccDecn.lsu[0]; + CAPTURE(lsu0); + CAPTURE(exp); + CAPTURE(sign); + if (exp == 0 && lsu0 == 15){ + //near pi/2 + cos_test(0.4); + } else if (exp == 0 && lsu0 == 47){ + //near 3/2 * pi + cos_test(0.4); + } else if (exp == 0 && lsu0 == 78){ + //near 5/2 * pi +// cos_test(0.4); + cos_test(1.1); //actual rtol is much worse than 0.4, random test happens to hit a bad one + } else { + cos_test(0.02); + } + } +} + +TEST_CASE("tan random"){ + std::default_random_engine gen; + std::uniform_int_distribution distrib(0,99); + std::uniform_int_distribution exp_distrib(-1,0); //restrict range for now + std::uniform_int_distribution sign_distrib(0,1); + for (int j = 0; j < NUM_RAND_TRIG_TESTS; j++){ + for (int i = 0; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + } + int exp = exp_distrib(gen); + int sign = sign_distrib(gen); + set_exponent(&AccDecn, exp, sign); + int lsu0 = AccDecn.lsu[0]; + CAPTURE(lsu0); + CAPTURE(exp); + CAPTURE(sign); + if (exp == -1 && lsu0 == 0){ + //very small + tan_test(40); + } else if ((exp == -1 && lsu0 < 10) || (exp == 0 && lsu0 == 0)){ + //small + tan_test(0.5); + } else if (exp == 0 && lsu0 == 15){ + //near pi/2 + tan_test(0.5); + } else if ((exp == 0 && lsu0 == 31)){ + //near pi + tan_test(0.2); + } else if (exp == 0 && lsu0 == 47){ + //near 3/2 * pi + tan_test(0.5); + } else if ((exp == 0 && lsu0 == 62)){ + //near 2pi + tan_test(0.2); + } else if (exp == 0 && lsu0 == 78){ + //near 5/2 * pi +// tan_test(0.5); + tan_test(0.6); //actual rtol is much worse than 0.4, random test happens to hit a bad one + } else if ((exp == 0 && lsu0 > 62)){ + //large + tan_test(0.1); + } else { + tan_test(0.02); + } + } +} From c6ac1e5e2c8574d7368777dbaf07aba1d41d984b Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Wed, 27 Jan 2021 21:11:32 -0500 Subject: [PATCH 10/10] add random tests for inverse trigonometric functions --- src/decn/decn_tests_trig.cpp | 97 ++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/decn/decn_tests_trig.cpp b/src/decn/decn_tests_trig.cpp index 3217287..af1166d 100644 --- a/src/decn/decn_tests_trig.cpp +++ b/src/decn/decn_tests_trig.cpp @@ -377,3 +377,100 @@ TEST_CASE("tan random"){ } } } + +TEST_CASE("atan random"){ + std::default_random_engine gen; + std::uniform_int_distribution distrib(0, 99); + std::uniform_int_distribution exp_distrib(-1, 0); //restrict range for now + std::uniform_int_distribution sign_distrib(0, 1); + for (int j = 0; j < NUM_RAND_TRIG_TESTS; j++){ + for (int i = 0; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + } + int exp = exp_distrib(gen); + int sign = sign_distrib(gen); + set_exponent(&AccDecn, exp, sign); + remove_leading_zeros(&AccDecn); + int lsu0 = AccDecn.lsu[0]; + exp = get_exponent(&AccDecn); + CAPTURE(lsu0); + CAPTURE(exp); + CAPTURE(sign); + if (exp <= -6){ + //extremely small + atan_test(10000); + } else if (exp < -1 || (exp == -1 && lsu0 == 0)){ + //very small + atan_test(100); + } else if ((exp == -1 && lsu0 < 10) || (exp == 0 && lsu0 == 0)){ + //small + atan_test(3); + } else { + atan_test(0.02); + } + } +} + +TEST_CASE("asin random"){ + std::default_random_engine gen; + std::uniform_int_distribution distrib(0, 99); + std::uniform_int_distribution exp_distrib(-2, -1); //restrict range for now + std::uniform_int_distribution sign_distrib(0, 1); + for (int j = 0; j < NUM_RAND_TRIG_TESTS; j++){ + for (int i = 0; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + } + int exp = exp_distrib(gen); + int sign = sign_distrib(gen); + set_exponent(&AccDecn, exp, sign); + remove_leading_zeros(&AccDecn); + int lsu0 = AccDecn.lsu[0]; + exp = get_exponent(&AccDecn); + CAPTURE(lsu0); + CAPTURE(exp); + CAPTURE(sign); + if (exp <= -7) { + //extremely small + asin_test(50000); + } else if (exp < -5) { + //very very small + asin_test(1000); + } else if (exp < -1 || (exp == -1 && lsu0 == 0)){ + //very small + asin_test(100); + } else if ((exp == -1 && lsu0 < 10) || (exp == 0 && lsu0 == 0)){ + //small + asin_test(0.5); + } else { + asin_test(0.02); + } + } +} + + +TEST_CASE("acos random"){ + std::default_random_engine gen; + std::uniform_int_distribution distrib(0, 99); + std::uniform_int_distribution exp_distrib(-2, -1); //restrict range for now + std::uniform_int_distribution sign_distrib(0, 1); + for (int j = 0; j < NUM_RAND_TRIG_TESTS; j++){ + for (int i = 0; i < DEC80_NUM_LSU; i++){ + AccDecn.lsu[i] = distrib(gen); + } + int exp = exp_distrib(gen); + int sign = sign_distrib(gen); + set_exponent(&AccDecn, exp, sign); + remove_leading_zeros(&AccDecn); + int lsu0 = AccDecn.lsu[0]; + exp = get_exponent(&AccDecn); + CAPTURE(lsu0); + CAPTURE(exp); + CAPTURE(sign); + if ((exp == -1 && lsu0 == 99)){ + //near 1 + acos_test(10); + } else { + acos_test(0.02); + } + } +}