Skip to content

Commit df8e447

Browse files
committed
feat(test_diagnosis): fraction/percent tolerances
This commit adds 1. operator(.withinFraction.) for real & double precision operands 2. operator(.withinPercentage.) for real & double precision operands 3. unit tests for each operator.
1 parent 4ae4800 commit df8e447

4 files changed

Lines changed: 160 additions & 25 deletions

File tree

src/julienne/julienne_test_diagnosis_m.F90

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ module julienne_test_diagnosis_m
1414
public :: operator(.and.)
1515
public :: operator(.approximates.)
1616
public :: operator(.within.)
17+
public :: operator(.withinFraction.)
18+
public :: operator(.withinPercentage.)
1719
public :: operator(.equalsExpected.)
1820
public :: operator(.lessThan.)
1921
public :: operator(.lessThanOrEqualTo.)
@@ -183,6 +185,50 @@ elemental module function within_double_precision(operands, tolerance) result(te
183185

184186
end interface
185187

188+
interface operator(.withinFraction.)
189+
190+
elemental module function within_real_fraction(operands, fractional_tolerance) result(test_diagnosis)
191+
implicit none
192+
type(operands_t), intent(in) :: operands
193+
real, intent(in) :: fractional_tolerance
194+
type(test_diagnosis_t) test_diagnosis
195+
end function
196+
197+
elemental module function within_double_precision_fraction(operands, fractional_tolerance) result(test_diagnosis)
198+
implicit none
199+
#if HAVE_DERIVED_TYPE_KIND_PARAMETERS
200+
type(operands_t(double_precision)), intent(in) :: operands
201+
#else
202+
type(double_precision_operands_t), intent(in) :: operands
203+
#endif
204+
double precision, intent(in) :: fractional_tolerance
205+
type(test_diagnosis_t) test_diagnosis
206+
end function
207+
208+
end interface
209+
210+
interface operator(.withinPercentage.)
211+
212+
elemental module function within_real_percentage(operands, percentage_tolerance) result(test_diagnosis)
213+
implicit none
214+
type(operands_t), intent(in) :: operands
215+
real, intent(in) :: percentage_tolerance
216+
type(test_diagnosis_t) test_diagnosis
217+
end function
218+
219+
elemental module function within_double_precision_percentage(operands, percentage_tolerance) result(test_diagnosis)
220+
implicit none
221+
#if HAVE_DERIVED_TYPE_KIND_PARAMETERS
222+
type(operands_t(double_precision)), intent(in) :: operands
223+
#else
224+
type(double_precision_operands_t), intent(in) :: operands
225+
#endif
226+
double precision, intent(in) :: percentage_tolerance
227+
type(test_diagnosis_t) test_diagnosis
228+
end function
229+
230+
end interface
231+
186232
interface test_diagnosis_t
187233

188234
elemental module function construct_from_string_t(test_passed, diagnostics_string) result(test_diagnosis)

src/julienne/julienne_test_diagnosis_s.F90

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,43 @@
164164
else
165165
test_diagnosis = test_diagnosis_t(test_passed=.false. &
166166
,diagnostics_string = "expected " // string_t(operands%expected) &
167-
// "within a tolerance of " // string_t(tolerance) &
167+
// " within a tolerance of " // string_t(tolerance) &
168168
// "; actual value is " // string_t(operands%actual) &
169169
)
170170
end if
171171

172172
end procedure
173173

174+
module procedure within_real_fraction
175+
176+
if (abs(operands%actual - operands%expected) <= abs(fractional_tolerance*operands%expected)) then
177+
! We use <= to allow for fractional_tolerance=0, which could never be satisfied if we used < instead:
178+
test_diagnosis = test_diagnosis_t(test_passed=.true., diagnostics_string="")
179+
else
180+
test_diagnosis = test_diagnosis_t(test_passed=.false. &
181+
,diagnostics_string = "expected " // string_t(operands%expected) &
182+
// " within a fractional tolerance of " // string_t(fractional_tolerance) &
183+
// "; actual value is " // string_t(operands%actual) &
184+
)
185+
end if
186+
187+
end procedure
188+
189+
module procedure within_real_percentage
190+
191+
if (abs(operands%actual - operands%expected) <= abs(operands%expected*percentage_tolerance/1D02)) then
192+
! We use <= to allow for fractional_tolerance=0, which could never be satisfied if we used < instead:
193+
test_diagnosis = test_diagnosis_t(test_passed=.true., diagnostics_string="")
194+
else
195+
test_diagnosis = test_diagnosis_t(test_passed=.false. &
196+
,diagnostics_string = "expected " // string_t(operands%expected) &
197+
// " within a tolerance of " // string_t(percentage_tolerance) // " percent;" &
198+
// " actual value is " // string_t(operands%actual) &
199+
)
200+
end if
201+
202+
end procedure
203+
174204
module procedure within_double_precision
175205

176206
if (abs(operands%actual - operands%expected) <= tolerance) then
@@ -179,13 +209,43 @@
179209
else
180210
test_diagnosis = test_diagnosis_t(test_passed=.false. &
181211
,diagnostics_string = "expected " // string_t(operands%expected) &
182-
// "within a tolerance of " // string_t(tolerance) &
212+
// " within a tolerance of " // string_t(tolerance) &
213+
// "; actual value is " // string_t(operands%actual) &
214+
)
215+
end if
216+
217+
end procedure
218+
219+
module procedure within_double_precision_fraction
220+
221+
if (abs(operands%actual - operands%expected) <= abs(fractional_tolerance*operands%expected)) then
222+
! We use <= to allow for tolerance=0, which could never be satisfied if we used < instead:
223+
test_diagnosis = test_diagnosis_t(test_passed=.true., diagnostics_string="")
224+
else
225+
test_diagnosis = test_diagnosis_t(test_passed=.false. &
226+
,diagnostics_string = "expected " // string_t(operands%expected) &
227+
// " within a fractional tolerance of " // string_t(fractional_tolerance) &
183228
// "; actual value is " // string_t(operands%actual) &
184229
)
185230
end if
186231

187232
end procedure
188233

234+
module procedure within_double_precision_percentage
235+
236+
if (abs((operands%actual - operands%expected)) <= abs(operands%expected*percentage_tolerance/1D02)) then
237+
! We use <= to allow for tolerance=0, which could never be satisfied if we used < instead:
238+
test_diagnosis = test_diagnosis_t(test_passed=.true., diagnostics_string="")
239+
else
240+
test_diagnosis = test_diagnosis_t(test_passed=.false. &
241+
,diagnostics_string = "expected " // string_t(operands%expected) &
242+
// " within a tolerance of " // string_t(percentage_tolerance) // " percent;" &
243+
// " actual value is " // string_t(operands%actual) &
244+
)
245+
end if
246+
247+
end procedure
248+
189249
module procedure construct_from_string_t
190250
test_diagnosis%test_passed_ = test_passed
191251
test_diagnosis%diagnostics_string_ = diagnostics_string

src/julienne_m.f90

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ module julienne_m
2020
,operator(.lessThan.) &
2121
,operator(.lessThanOrEqualTo.) &
2222
,operator(.greaterThan.) &
23-
,operator(.greaterThanOrEqualTo.)
23+
,operator(.greaterThanOrEqualTo.) &
24+
,operator(.withinFraction.) &
25+
,operator(.withinPercentage.)
2426
use julienne_test_result_m, only : test_result_t
2527
use julienne_vector_test_description_m, only : vector_test_description_t, vector_diagnosis_function_i
2628
implicit none

test/test_diagnosis_test_m.F90

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ module test_diagnosis_test_m
2121
,operator(.equalsExpected.) &
2222
,operator(.approximates.) &
2323
,operator(.within.) &
24+
,operator(.withinFraction.) &
25+
,operator(.withinPercentage.) &
2426
,operator(.lessThan.) &
2527
,operator(.lessThanOrEqualTo.) &
2628
,operator(.greaterThan.) &
@@ -50,6 +52,10 @@ function results() result(test_results)
5052
associate(descriptions => [ &
5153
test_description_t("construction from a real expression of the form 'x .approximates. y .within. tolerance'", check_approximates_real) &
5254
,test_description_t("construction from a double precision expression of the form 'x .approximates. y .within. tolerance'", check_approximates_double) &
55+
,test_description_t("construction from the real expression 'x .approximates. y .withinFraction. tolerance'", check_approximates_real_fraction) &
56+
,test_description_t("construction from the double precision expression 'x .approximates. y .withinFraction. tolerance'", check_approximates_double_fraction) &
57+
,test_description_t("construction from the real expression 'x .approximates. y .withinPercentage. tolerance'", check_approximates_real_percentage) &
58+
,test_description_t("construction from the double precision expression 'x .approximates. y .withinPercentage. tolerance'", check_approximates_double_percentage) &
5359
,test_description_t("construction from an integer expression of the form 'i .equalsExpected. j", check_equals_integer) &
5460
,test_description_t("construction from a real expression of the form 'x .lessThan. y", check_less_than_real) &
5561
,test_description_t("construction from a double precision expression of the form 'x .lessThan. y", check_less_than_double) &
@@ -66,32 +72,29 @@ function results() result(test_results)
6672
! Work around missing Fortran 2008 feature: associating a procedure actual argument with a procedure pointer dummy argument:
6773
type(test_description_t), allocatable :: descriptions(:)
6874
procedure(diagnosis_function_i), pointer :: &
69-
check_approximates_real_ptr &
70-
,check_approximates_double_ptr , check_equals_integer_ptr &
71-
,check_less_than_real_ptr , check_greater_than_real_ptr &
72-
,check_less_than_double_ptr , check_greater_than_double_ptr &
73-
,check_less_than_integer_ptr , check_greater_than_integer_ptr &
74-
,check_less_than_or_equal_to_integer_ptr, check_greater_than_or_equal_to_integer_ptr &
75-
,check_and_with_scalar_operands_ptr &
76-
,check_and_with_vector_operands_ptr
77-
78-
check_approximates_real_ptr => check_approximates_real
79-
check_approximates_double_ptr => check_approximates_double
80-
check_equals_integer_ptr => check_equals_integer
81-
check_less_than_real_ptr => check_less_than_real
82-
check_less_than_double_ptr => check_less_than_double
83-
check_less_than_integer_ptr => check_less_than_integer
84-
check_less_than_or_equal_to_integer_ptr => check_less_than_or_equal_to_integer
85-
check_greater_than_real_ptr => check_greater_than_real
86-
check_greater_than_double_ptr => check_greater_than_double
87-
check_greater_than_integer_ptr => check_greater_than_integer
88-
check_greater_than_or_equal_to_integer_ptr => check_greater_than_or_equal_to_integer
89-
check_and_with_scalar_operands_ptr => check_and_with_scalar_operands
90-
check_and_with_vector_operands_ptr => check_and_with_vector_operands
75+
check_approximates_real_ptr => check_approximates_real &
76+
,check_approximates_double_ptr => check_approximates_double &
77+
,check_approximates_real_fraction_ptr => check_approximates_real_fraction &
78+
,check_approximates_double_fraction_ptr => check_approximates_double_fraction &
79+
,check_approximates_real_percentage_ptr => check_approximates_real_percentage &
80+
,check_approximates_double_percentage_ptr => check_approximates_double_percentage &
81+
,check_equals_integer_ptr => check_equals_integer &
82+
,check_less_than_real_ptr => check_less_than_real &
83+
,check_less_than_double_ptr => check_less_than_double &
84+
,check_less_than_integer_ptr => check_less_than_integer &
85+
,check_less_than_or_equal_to_integer_ptr => check_less_than_or_equal_to_integer &
86+
,check_greater_than_real_ptr => check_greater_than_real &
87+
,check_greater_than_double_ptr => check_greater_than_double &
88+
,check_greater_than_integer_ptr => check_greater_than_integer &
89+
,check_greater_than_or_equal_to_integer_ptr => check_greater_than_or_equal_to_integer &
90+
,check_and_with_scalar_operands_ptr => check_and_with_scalar_operands &
91+
,check_and_with_vector_operands_ptr => check_and_with_vector_operands
9192

9293
descriptions = [ &
9394
test_description_t("construction from a real expression of the form `x .approximates. y .within. tolerance`" , check_approximates_real_ptr) &
9495
,test_description_t("construction from a double-precision expression of the form `x .approximates. y .within. tolerance`", check_approximates_double_ptr) &
96+
test_description_t("construction from the real expression 'x .approximates. y .withinFraction. tolerance'", check_approximates_real_fraction_ptr) &
97+
,test_description_t("construction from the double precision expression 'x .approximates. y .withinFraction. tolerance'", check_approximates_double_fraction_ptr) &
9598
,test_description_t("construction from an integer expression of the form `i .equalsExpected. j`" , check_equals_integer_ptr) &
9699
,test_description_t("construction from a real expression of the form 'x .lessThan. y" , check_less_than_real_ptr) &
97100
,test_description_t("construction from a double precision expression of the form 'x .lessThan. y" , check_less_than_double_ptr) &
@@ -143,6 +146,30 @@ function check_approximates_double() result(test_diagnosis)
143146
test_diagnosis = 1D0 .approximates. expected_value .within. tolerance
144147
end function
145148

149+
function check_approximates_real_fraction() result(test_diagnosis)
150+
type(test_diagnosis_t) test_diagnosis
151+
real, parameter :: actual_value = 1.1, expected_value = 1., fraction_ = 2.E-01
152+
test_diagnosis = actual_value .approximates. expected_value .withinFraction. fraction_
153+
end function
154+
155+
function check_approximates_double_fraction() result(test_diagnosis)
156+
type(test_diagnosis_t) test_diagnosis
157+
double precision, parameter :: actual_value = 1.1D0, expected_value = 1D0, fraction_ = 2D-01
158+
test_diagnosis = actual_value .approximates. expected_value .withinFraction. fraction_
159+
end function
160+
161+
function check_approximates_real_percentage() result(test_diagnosis)
162+
type(test_diagnosis_t) test_diagnosis
163+
real, parameter :: actual_value = 1.01, expected_value = 1., percentage = 2.
164+
test_diagnosis = actual_value .approximates. expected_value .withinPercentage. percentage
165+
end function
166+
167+
function check_approximates_double_percentage() result(test_diagnosis)
168+
type(test_diagnosis_t) test_diagnosis
169+
double precision, parameter :: actual_value = 1.01D0, expected_value = 1D0, percentage = 2D0
170+
test_diagnosis = actual_value .approximates. expected_value .withinPercentage. percentage
171+
end function
172+
146173
function check_equals_integer() result(test_diagnosis)
147174
type(test_diagnosis_t) test_diagnosis
148175
integer, parameter :: expected_value = 1

0 commit comments

Comments
 (0)