Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quick implementation of "is_nearly_double" #246

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/cgreen/assertions.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ namespace cgreen {
/* Utility: */
int get_significant_figures(void);
void significant_figures_for_assert_double_are(int figures);
void double_relative_tolerance_is(double tolerance);
double get_double_relative_tolerance(void);
void double_absolute_tolerance_is(double tolerance);
double get_double_absolute_tolerance(void);

#include <cgreen/legacy.h>

Expand Down
1 change: 1 addition & 0 deletions include/cgreen/constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Constraint *create_equal_to_double_constraint(double expected_value, const char
Constraint *create_not_equal_to_double_constraint(double expected_value, const char *expected_value_name);
Constraint *create_less_than_double_constraint(double expected_value, const char *expected_value_name);
Constraint *create_greater_than_double_constraint(double expected_value, const char *expected_value_name);
Constraint *create_nearly_double_constraint(double expected_value, const char *expected_value_name);
Constraint *create_return_value_constraint(intptr_t value_to_return);
Constraint *create_return_by_value_constraint(intptr_t value_to_return, size_t size);
Constraint *create_return_double_value_constraint(double value_to_return);
Expand Down
1 change: 1 addition & 0 deletions include/cgreen/constraint_syntax_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#define is_less_than_double(value) create_less_than_double_constraint(value, #value)
#define is_greater_than_double(value) create_greater_than_double_constraint(value, #value)

#define is_nearly_double(value) create_nearly_double_constraint(value, #value)

#define with_side_effect(callback, data) create_with_side_effect_constraint(callback, data)
#define will_return(value) create_return_value_constraint((intptr_t)value)
Expand Down
19 changes: 18 additions & 1 deletion src/assertions.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,23 @@ void assert_that_double_(const char *file, int line, const char *expression, dou

boxed_actual = (BoxedDouble*)box_double(actual);

(*get_test_reporter()->assert_true)(get_test_reporter(), file, line,
if (!strcmp(constraint->name, "be nearly double")) {
(*get_test_reporter()->assert_true)(get_test_reporter(), file, line,
(*constraint->compare)(constraint, make_cgreen_double_value(actual)),
"Expected [%s] to [%s] [%s] with [%g] relative and [%g] absolute tolerance\n"
"\t\tactual value:\t\t\t[%g]\n"
"\t\texpected value:\t\t\t[%g]\n"
"\t\tactual - expected:\t\t[%g]\n",
expression,
constraint->name,
constraint->expected_value_name,
get_double_relative_tolerance(),
get_double_absolute_tolerance(),
actual,
constraint->expected_value.value.double_value,
actual - constraint->expected_value.value.double_value);
} else {
(*get_test_reporter()->assert_true)(get_test_reporter(), file, line,
(*constraint->compare)(constraint, make_cgreen_double_value(actual)),
"Expected [%s] to [%s] [%s] within [%d] significant figures\n"
"\t\tactual value:\t\t\t[%08f]\n"
Expand All @@ -122,6 +138,7 @@ void assert_that_double_(const char *file, int line, const char *expression, dou
get_significant_figures(),
actual,
constraint->expected_value.value.double_value);
}

free(boxed_actual);
constraint->destroy(constraint);
Expand Down
75 changes: 64 additions & 11 deletions src/constraint.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,10 @@
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) > (b) ? (b) : (a))


static int significant_figures = 8;
static double absolute_tolerance = DBL_MIN / 1.0e-8;


static double accuracy(int significant_figures, double largest);
static double double_absolute_tolerance = 1e-15;
static double double_relative_tolerance = 1e-7;

static bool compare_want_greater_value(Constraint *constraint, CgreenValue actual);

Expand Down Expand Up @@ -77,6 +75,9 @@ static void test_do_not_want_double(Constraint *constraint, const char *function
const char *test_file, int test_line, TestReporter *reporter);
static bool compare_want_lesser_double(Constraint *constraint, CgreenValue actual);
static bool compare_want_greater_double(Constraint *constraint, CgreenValue actual);
static bool compare_want_nearly_double(Constraint *constraint, CgreenValue actual);
static void test_want_nearly_double(Constraint *constraint, const char *function, CgreenValue actual,
const char *test_file, int test_line, TestReporter *reporter);

static void set_contents(Constraint *constraint, const char *function, CgreenValue actual,
const char *test_file, int test_line, TestReporter *reporter);
Expand Down Expand Up @@ -440,7 +441,18 @@ Constraint *create_greater_than_double_constraint(double expected_value, const c
constraint->execute = &test_true;
constraint->name = "be greater than double";
constraint->destroy = &destroy_double_constraint;
constraint->expected_value_message = "\t\texpected to be greater than:\t[%08f]";

return constraint;
}

Constraint *create_nearly_double_constraint(double expected_value, const char *expected_value_name) {
Constraint *constraint = create_constraint_expecting(make_cgreen_double_value(expected_value), expected_value_name);
constraint->type = CGREEN_DOUBLE_COMPARER_CONSTRAINT;

constraint->compare = &compare_want_nearly_double;
constraint->execute = &test_want_nearly_double;
constraint->name = "be nearly double";
constraint->destroy = &destroy_double_constraint;

return constraint;
}
Expand Down Expand Up @@ -684,6 +696,10 @@ static bool compare_want_greater_double(Constraint *constraint, CgreenValue actu
return double_is_greater(constraint->expected_value.value.double_value, actual.value.double_value);
}

static bool compare_want_nearly_double(Constraint *constraint, CgreenValue actual) {
return doubles_are_nearly(constraint->expected_value.value.double_value, actual.value.double_value);
}

static void test_want_double(Constraint *constraint, const char *function, CgreenValue actual,
const char *test_file, int test_line, TestReporter *reporter) {
(*reporter->assert_true)(
Expand Down Expand Up @@ -716,6 +732,20 @@ static void test_do_not_want_double(Constraint *constraint, const char *function
constraint->parameter_name);
}

static void test_want_nearly_double(Constraint *constraint, const char *function, CgreenValue actual,
const char *test_file, int test_line, TestReporter *reporter) {
(*reporter->assert_true)(
reporter,
test_file,
test_line,
(*constraint->compare)(constraint, actual),
"Wanted [%f], but got [%f] in function [%s] parameter [%s]",
constraint->expected_value.value.double_value,
actual.value.double_value,
function,
constraint->parameter_name);
}

void destroy_double_constraint(Constraint *constraint) {
destroy_empty_constraint(constraint);
}
Expand Down Expand Up @@ -813,22 +843,45 @@ bool constraint_is_for_parameter_in(const Constraint *constraint, const char *na
return found;
}

static double accuracy(double largest) {
return pow(10.0, 1.0 + floor(log10(fabs(largest))) - significant_figures);
}

bool doubles_are_equal(double tried, double expected) {
double abs_diff = fabs(tried - expected);
if (abs_diff < absolute_tolerance) return true;
return abs_diff < accuracy(significant_figures, max(fabs(tried), fabs(expected)));
if (abs_diff < double_absolute_tolerance) return true;
double max_abs = max(fabs(tried), fabs(expected));
return abs_diff < accuracy(max_abs);
}

bool double_is_lesser(double actual, double expected) {
return expected < actual + accuracy(significant_figures, max(actual, expected));
return expected < actual + accuracy(max(actual, expected));
}

bool double_is_greater(double actual, double expected) {
return expected > actual - accuracy(significant_figures, max(actual, expected));
return expected > actual - accuracy(max(actual, expected));
}

bool doubles_are_nearly(double tried, double expected) {
double abs_diff = fabs(tried - expected);
double abs_max = fmax(fabs(tried), fabs(expected));
return abs_diff < double_relative_tolerance*abs_max + double_absolute_tolerance;
}

void double_relative_tolerance_is(double tol) {
double_relative_tolerance = tol;
}

double get_double_relative_tolerance(void) {
return double_relative_tolerance;
}

void double_absolute_tolerance_is(double tol) {
double_absolute_tolerance = tol;
}

static double accuracy(int figures, double largest) {
return pow(10.0, 1.0 + floor(log10(fabs(largest))) - figures);
double get_double_absolute_tolerance(void) {
return double_absolute_tolerance;
}

void significant_figures_for_assert_double_are(int figures) {
Expand Down
1 change: 1 addition & 0 deletions src/constraint_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ extern bool constraint_is_for_parameter_in(const Constraint *, const char *);
extern bool doubles_are_equal(double tried, double expected);
extern bool double_is_lesser(double actual, double expected);
extern bool double_is_greater(double actual, double expected);
extern bool doubles_are_nearly(double tried, double expected);


#ifdef __cplusplus
Expand Down