increase test coverage

This commit is contained in:
Jeff Wang 2019-11-20 01:54:04 -05:00
parent 38c4e4678c
commit b04a95e420
5 changed files with 287 additions and 73 deletions

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.1)
# 3rd party tools
find_package(Qt5 COMPONENTS Widgets Qml Quick REQUIRED)

View File

@ -1,12 +1,21 @@
#code coverage
add_library(coverage_config INTERFACE)
target_compile_options(coverage_config INTERFACE -O0 -g --coverage)
target_link_libraries(coverage_config INTERFACE --coverage)
# decn library
add_library(decn decn.c)
# decn library with coverage
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)
target_link_libraries(decn_test decn_cover coverage_config)
add_executable(decn_tests catch_main.cpp decn_tests.cpp ../utils.c)
target_link_libraries(decn_tests decn mpfr)
target_link_libraries(decn_tests decn_cover coverage_config mpfr)
# decn prototyping
add_subdirectory(proto)

View File

@ -30,9 +30,9 @@
// #define DEBUG_MULT
// #define DEBUG_MULT_ALL //even more verbose
// #define DEBUG_DIV
#define DEBUG_LOG
// #define DEBUG_LOG
// #define DEBUG_LOG_ALL //even more verbose
#define DEBUG_EXP
// #define DEBUG_EXP
// #define DEBUG_EXP_ALL //even more verbose
#ifndef DESKTOP
@ -211,6 +211,7 @@ void build_dec80(__xdata const char* signif_str, exp_t exponent){
return;
} else if (signif_str[0] == '-'){
curr_sign = SIGN_NEG_ZERO;
i++;
}
//go through digits
@ -293,7 +294,7 @@ void build_dec80(__xdata const char* signif_str, exp_t exponent){
return;
} else {
//not zero
int8_t new_exponent;
exp_t new_exponent = exponent;
//write out saved nibble, if it exists
// (saved while nibble_i even, nibble_i then incremented to odd)
if (nibble_i & 1){ //odd
@ -307,37 +308,28 @@ void build_dec80(__xdata const char* signif_str, exp_t exponent){
if (num_lr_points > 0){ //left count exists
assert(DEC80_NUM_LSU*2 > num_lr_points);
new_exponent = exponent + (num_lr_points - 1); //1 digit left of implicit point
//check for overflow
#ifdef EXTRA_CHECKS
if (new_exponent < exponent || exponent > DEC80_MAX_EXP){
#ifdef DEBUG
printf(" overflow (new_exp, exp)=(%d,%d)\n",
new_exponent, exponent);
#endif
set_dec80_NaN(&AccDecn);
return;
}
#endif
//overflow is checked later, should be impossible to overflow int16_t:
assert(new_exponent >= exponent);
} else if (num_lr_points < 0) { //right count exists
// (num_lr_points represents exponent shift)
// (this ends up being a subtraction)
new_exponent = exponent + num_lr_points;
//check for underflow
#ifdef EXTRA_CHECKS
if (new_exponent > exponent || exponent < DEC80_MIN_EXP){
#ifdef DEBUG
printf(" underflow (new_exp, exp)=(%d,%d)\n",
new_exponent, exponent);
#endif
set_dec80_NaN(&AccDecn);
return;
}
#endif
} else {
//no change
new_exponent = exponent;
//underflow is checked later, should be impossible to overflow int16_t:
assert(new_exponent <= exponent);
}
//check for over/underflow of exponent
exponent = new_exponent;
#ifdef EXTRA_CHECKS
if (exponent > DEC80_MAX_EXP || exponent < DEC80_MIN_EXP){
#ifdef DEBUG
printf(" over/underflow (new_exp, exp)=(%d,%d)\n",
new_exponent, exponent);
#endif
set_dec80_NaN(&AccDecn);
return;
}
#endif
//set negative bit
set_exponent(&AccDecn, exponent, IS_NEG(curr_sign));
//normalize
@ -354,6 +346,12 @@ void build_dec80(__xdata const char* signif_str, exp_t exponent){
return;
}
} else { //invalid character
#ifdef DEBUG
printf(" invalid character %c at i=%d\n", signif_str[i], i);
#endif
set_dec80_NaN(&AccDecn);
return;
}
i++;
assert(i < DECN_BUF_SIZE);
@ -439,7 +437,7 @@ static int8_t compare_magn(void){ //returns a<b: -1, a==b: 0, a>b: 1
//compare signifcands while tracking magnitude
for (
a_i = 0, b_i = 0;
a_i < DEC80_NUM_LSU && b_i < DEC80_NUM_LSU;
a_i < DEC80_NUM_LSU;
a_i++, b_i++, a_exp+=2, b_exp+=2
)
{
@ -662,26 +660,17 @@ void add_decn(void){
uint8_t digit100 = AccDecn.lsu[i] + BDecn.lsu[i] + carry;
AccDecn.lsu[i] = digit100 % 100;
carry = digit100 / 100;
assert(carry < 100);
assert(carry <= 1);
}
//may need to rescale number
if (carry > 0){
assert(carry == 1);
exp_t curr_exp = get_exponent(&AccDecn);
rel = (AccDecn.exponent < 0); //is_neg?
#ifdef DEBUG_ADD
printf(" carry out: %d", carry);
#endif
//shift right
if (carry < 10){
shift_right(&AccDecn);
AccDecn.lsu[0] += carry*10; //carry gets shifted into most significant digit
curr_exp++;
} else {
shift_right(&AccDecn);
shift_right(&AccDecn);
AccDecn.lsu[0] = carry;
curr_exp+=2;
}
shift_right(&AccDecn);
AccDecn.lsu[0] += 10; //carry gets shifted into most significant digit
curr_exp++;
//track sign
set_exponent(&AccDecn, curr_exp, rel); //rel==is_neg?
}
@ -715,7 +704,8 @@ void mult_decn(void){
//calculate new exponent
new_exponent = get_exponent(&AccDecn) + get_exponent(&BDecn);
#ifdef DEBUG_MULT
printf("\n new exponent: %d, is_neg: %u", new_exponent, is_neg);
printf("\n a_exp: %d, b_exp: %d", get_exponent(&AccDecn), get_exponent(&BDecn));
printf("\n new exponent: %d, is_neg: %u", new_exponent, is_neg);
#endif
//do multiply
for (i = DEC80_NUM_LSU - 1; i >= 0; i--){
@ -1079,13 +1069,11 @@ void exp_decn(void){
//check if in range
copy_decn(&SAVED, &AccDecn); //save = accum
set_dec80_zero(&BDecn);
BDecn.lsu[0] = 23;
BDecn.lsu[1] = 02;
BDecn.lsu[2] = 58;
BDecn.lsu[2] = 51;
BDecn.exponent = 2; //b = 230.25851
BDecn.lsu[0] = 29;
BDecn.lsu[1] = 47;
BDecn.exponent = 2; //b = 294.7
negate_decn(&BDecn);
add_decn(); //accum = x - 230.25851 (should be negative if in range)
add_decn(); //accum = x - 294.7 (should be negative if in range)
if (!(AccDecn.exponent < 0)){ //if not negative
set_dec80_NaN(&AccDecn);
return;
@ -1249,7 +1237,12 @@ static void set_str_error(void){
Buf[5] = '\0';
}
int8_t decn_to_str(const dec80* x){
#ifdef DESKTOP
int
#else
int8_t
#endif
decn_to_str(const dec80* x){
#define INSERT_DOT() Buf[i++]='.'
uint8_t i = 0;
uint8_t digit100;
@ -1377,7 +1370,11 @@ int8_t decn_to_str(const dec80* x){
//print exponent
if (use_sci){
//check for overflow
#ifdef DESKTOP
if (exponent > DEC80_MAX_EXP || exponent < DEC80_MIN_EXP){
#else
if (exponent > DECN_MAX_PRINT_EXP || exponent < DECN_MIN_PRINT_EXP){
#endif
set_str_error();
return 0;
}
@ -1398,7 +1395,7 @@ int8_t decn_to_str(const dec80* x){
#ifdef DESKTOP
//complete string including exponent
void decn_to_str_complete(const dec80* x){
int8_t exponent = decn_to_str(x);
int exponent = decn_to_str(x);
int i;
//find end of string
for (i = 0; Buf[i] != '\0'; i++);

View File

@ -107,7 +107,13 @@ void exp10_decn(void);
//Buf should hold at least 18 + 4 + 5 + 1 = 28
#define DECN_BUF_SIZE 28
extern __xdata char Buf[DECN_BUF_SIZE];
int8_t decn_to_str(const dec80* x);
#ifdef DESKTOP
int
#else
int8_t
#endif
decn_to_str(const dec80* x);
#ifdef DESKTOP
//complete string including exponent

View File

@ -20,10 +20,11 @@
*/
#include <string.h>
#include <string>
#include <boost/multiprecision/mpfr.hpp>
#include <catch.hpp>
#include "decn.h"
#include "../utils.h"
namespace bmp = boost::multiprecision;
@ -46,6 +47,119 @@ TEST_CASE("build decn"){
negate_decn(&AccDecn);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-9234.567890123456"));
//small positive
build_dec80(".1", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("100."));
build_dec80("0.1", -1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0.01"));
build_dec80("0.01", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("10."));
//zero
build_dec80(".", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
build_dec80(".0", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
build_dec80("0.", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
build_dec80("-0.0", -1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
build_dec80("0", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
//small negative
build_dec80("-.1", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-100."));
build_dec80("-0.1", -1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-0.01"));
build_dec80("-0.001", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-1."));
//empty string -> 0
build_dec80("", 90);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("0"));
//too many .
build_dec80("..", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
//very long (truncated)
build_dec80("12345678901234567890", -2);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("123456789012345678."));
//overflow
build_dec80("100", DEC80_MAX_EXP-1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
build_dec80("1", DEC80_MAX_EXP+1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
build_dec80("0.1", DEC80_MAX_EXP+2);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
//underflow
build_dec80("10", DEC80_MIN_EXP-2);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
build_dec80("1", DEC80_MIN_EXP-1);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
build_dec80("0.3", DEC80_MIN_EXP);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
CHECK(decn_is_nan(&AccDecn) == 1);
//left/right count
build_dec80("-100.001", 3);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-100001."));
//invalid
build_dec80(":", 0);
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error"));
//special number that is not NaN
AccDecn.lsu[1] = 3;
CHECK(decn_is_nan(&AccDecn) == 0);
}
TEST_CASE("build_large"){
int large_exp = DEC80_MAX_EXP/2 - 50;
build_dec80("9.99", large_exp);
decn_to_str_complete(&AccDecn);
CHECK(AccDecn.exponent == large_exp);
std::string expected = "9.99E";
expected += std::to_string(large_exp);
CHECK_THAT(Buf, Equals(expected));
}
TEST_CASE("small fractions >= 1/10"){
@ -91,6 +205,24 @@ TEST_CASE("add"){
decn_to_str_complete(&AccDecn);
//compare result of new acc - b
CHECK_THAT(Buf, Equals("-9234.567890123456")); //acc-b
//add 0
build_decn_at(&BDecn, "0", 0);
add_decn();
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-9234.567890123456")); //same
//carry into MSB
build_dec80( "-82345678901234567.8", -1);
build_decn_at(&BDecn, "-87654321098765432.2", -1);
add_decn();
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf,Equals("-17000000000000000.")); //acc+b
//don't negate NaN
set_dec80_NaN(&AccDecn);
negate_decn(&AccDecn);
CHECK(decn_is_nan(&AccDecn));
}
TEST_CASE("multiply"){
@ -103,6 +235,14 @@ TEST_CASE("multiply"){
mult_decn();
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("-8527724.41172991849")); //acc*b
//overflow
build_dec80("9.99", DEC80_MAX_EXP/2);
build_decn_at(&BDecn, "9.99", DEC80_MAX_EXP/2);
mult_decn();
decn_to_str_complete(&AccDecn);
CHECK_THAT(Buf, Equals("Error")); //acc*b
}
static void div_test(
@ -126,7 +266,6 @@ static void div_test(
//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);
@ -135,12 +274,24 @@ static void div_test(
// CAPTURE(b_full_str);
bmp::mpfr_float a_actual(a_full_str);
bmp::mpfr_float b_actual(b_full_str);
a_actual /= b_actual;
bmp::mpfr_float rel_diff = abs((a_actual - calculated) / a_actual);
CHECK(rel_diff < 1e-17);
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
@ -198,22 +349,29 @@ static void log_test(
//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);
// CAPTURE(x_full_str);
x_full_str += "e" + std::to_string(x_exp);
CAPTURE(x_full_str);
bmp::mpfr_float x_actual(x_full_str);
if (base10){
x_actual = log10(x_actual);
} else {
x_actual = log(x_actual);
}
CAPTURE(x_actual);
bmp::mpfr_float rel_diff = abs((x_actual - calculated) / x_actual);
CHECK(rel_diff < 3e-16); //TODO
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);
@ -221,19 +379,27 @@ TEST_CASE("log"){
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
double epsilon=6e-16,
bool base10=false
)
{
CAPTURE(x_str); CAPTURE(x_exp);
CAPTURE(base10);
build_dec80(x_str, x_exp);
exp_decn();
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);
@ -241,12 +407,24 @@ static void exp_test(
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);
@ -257,6 +435,25 @@ TEST_CASE("exp"){
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(
@ -306,3 +503,8 @@ TEST_CASE("power"){
"201", 0
);
}
TEST_CASE("u32str corner"){
u32str(0, &Buf[0], 10);
CHECK_THAT(Buf, Equals("0"));
}