Merge branch 'jjj11x/parallel_random_test'
add lots of random tests for decn library
This commit is contained in:
commit
1e6d786482
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@ -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
|
||||
|
||||
|
@ -18,6 +18,9 @@ else()
|
||||
add_compile_options(-Wall -Wextra -pedantic)
|
||||
endif()
|
||||
|
||||
# CTest Catch2 tests
|
||||
enable_testing()
|
||||
|
||||
# Directory with source code
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(qt_gui)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
@ -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).
|
||||
|
||||
|
@ -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,37 @@ 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_div_sqrt.cpp
|
||||
decn_tests_transcendental.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)
|
||||
|
@ -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 <catch2/catch.hpp>
|
||||
|
@ -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;
|
||||
@ -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);
|
||||
|
@ -60,6 +60,11 @@ 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 remove_leading_zeros(dec80* x);
|
||||
|
||||
|
||||
void copy_decn(dec80* const dest, const dec80* const src);
|
||||
|
||||
extern dec80 AccDecn;
|
||||
|
@ -21,11 +21,14 @@
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <boost/multiprecision/mpfr.hpp>
|
||||
#include <catch.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
#include "decn.h"
|
||||
#include "../utils.h"
|
||||
|
||||
#include "decn_tests.h"
|
||||
|
||||
|
||||
namespace bmp = boost::multiprecision;
|
||||
using Catch::Matchers::Equals;
|
||||
@ -258,333 +261,6 @@ TEST_CASE("multiply"){
|
||||
CHECK_THAT(Buf, Equals("Error")); //acc*b
|
||||
}
|
||||
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
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();
|
||||
decn_to_str_complete(&AccDecn);
|
||||
CAPTURE(Buf); // sqrt(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 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);
|
||||
bmp::mpfr_float calculated(Buf);
|
||||
bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual);
|
||||
CHECK(rel_diff < 3e-16); //TODO
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
CAPTURE(base10);
|
||||
build_dec80(x_str, x_exp);
|
||||
if (base10){
|
||||
exp10_decn();
|
||||
} else {
|
||||
exp_decn();
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 exp10_test(
|
||||
//input
|
||||
const char* x_str, int x_exp,
|
||||
double epsilon=3e-15
|
||||
)
|
||||
{
|
||||
exp_test(x_str, x_exp, epsilon, 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("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);
|
||||
}
|
||||
|
||||
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_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);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE("u32str corner"){
|
||||
u32str(0, &Buf[0], 10);
|
||||
CHECK_THAT(Buf, Equals("0"));
|
||||
|
28
src/decn/decn_tests.h
Normal file
28
src/decn/decn_tests.h
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
* decn_tests.h
|
||||
*
|
||||
* Created on: Oct 26, 2020
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DECN_TESTS_H_
|
||||
#define DECN_TESTS_H_
|
||||
|
||||
|
||||
static const int NUM_RAND_TESTS = 123456;
|
||||
|
||||
|
||||
#endif
|
200
src/decn/decn_tests_div_sqrt.cpp
Normal file
200
src/decn/decn_tests_div_sqrt.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
* 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 <string>
|
||||
#include <random>
|
||||
#include <boost/multiprecision/mpfr.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
#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<int> distrib(0, 99);
|
||||
std::uniform_int_distribution<int> 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<int> distribution(0,99);
|
||||
std::uniform_int_distribution<int> exp_distrib(-99,99);
|
||||
std::uniform_int_distribution<int> 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();
|
||||
}
|
||||
}
|
490
src/decn/decn_tests_transcendental.cpp
Normal file
490
src/decn/decn_tests_transcendental.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
* decn_tests_transcendental.cpp
|
||||
*
|
||||
* Unit tests using https://github.com/catchorg/Catch2
|
||||
*
|
||||
* separate out transcendental function tests
|
||||
*
|
||||
* Created on: Oct 26, 2020
|
||||
*/
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <boost/multiprecision/mpfr.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
#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<int> distrib(0,99);
|
||||
std::uniform_int_distribution<int> exp_distrib(-99,99);
|
||||
std::uniform_int_distribution<int> 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<int> lsu0_distrib(lsu0_low, lsu0_high);
|
||||
std::uniform_int_distribution<int> distrib(0,99);
|
||||
std::uniform_int_distribution<int> exp_distrib(-99,99);
|
||||
std::uniform_int_distribution<int> 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<int> distrib(0, 99);
|
||||
std::uniform_int_distribution<int> lsu0_high_distrib(0, 23);
|
||||
std::uniform_int_distribution<int> exp_distrib(exp_distrib_low, 2);
|
||||
std::uniform_int_distribution<int> 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<int> lsu0_distrib(lsu0_low, lsu0_high);
|
||||
std::uniform_int_distribution<int> distrib(0, 99);
|
||||
std::uniform_int_distribution<int> exp_distrib(exp_low, exp_high);
|
||||
std::uniform_int_distribution<int> 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<double>();
|
||||
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<int> 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);
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <boost/multiprecision/mpfr.hpp>
|
||||
#include <catch.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
#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,219 @@ 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<int> distrib(0,99);
|
||||
std::uniform_int_distribution<int> exp_distrib(-1,0); //restrict range for now
|
||||
std::uniform_int_distribution<int> 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<int> distrib(0,99);
|
||||
std::uniform_int_distribution<int> exp_distrib(-1,0); //restrict range for now
|
||||
std::uniform_int_distribution<int> 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<int> distrib(0,99);
|
||||
std::uniform_int_distribution<int> exp_distrib(-1,0); //restrict range for now
|
||||
std::uniform_int_distribution<int> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("atan random"){
|
||||
std::default_random_engine gen;
|
||||
std::uniform_int_distribution<int> distrib(0, 99);
|
||||
std::uniform_int_distribution<int> exp_distrib(-1, 0); //restrict range for now
|
||||
std::uniform_int_distribution<int> 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<int> distrib(0, 99);
|
||||
std::uniform_int_distribution<int> exp_distrib(-2, -1); //restrict range for now
|
||||
std::uniform_int_distribution<int> 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<int> distrib(0, 99);
|
||||
std::uniform_int_distribution<int> exp_distrib(-2, -1); //restrict range for now
|
||||
std::uniform_int_distribution<int> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user