Finished the latest version of autotesting (utility library). Uses ulps to determine the acceptable error instead of the relative error used previously

This commit is contained in:
jpekkila
2019-12-14 07:27:11 +02:00
parent 5ec2f6ad75
commit 22a3105068

View File

@@ -94,6 +94,11 @@ get_minimum_magnitude(const AcReal* field, const AcMeshInfo info)
return minimum; return minimum;
} }
// Get the maximum absolute error. Works well if all the values in the mesh are approximately
// in the same range.
// Finding the maximum ulp error is not useful, as it picks up on the noise beyond the
// floating-point precision range and gives huge errors with values that should be considered
// zero (f.ex. 1e-19 and 1e-22 give error of around 1e4 ulps)
static Error static Error
get_max_abs_error(const VertexBufferHandle vtxbuf_handle, const AcMesh model_mesh, get_max_abs_error(const VertexBufferHandle vtxbuf_handle, const AcMesh model_mesh,
const AcMesh candidate_mesh) const AcMesh candidate_mesh)
@@ -131,37 +136,54 @@ print_error_to_file(const char* path, const int n, const Error error)
static bool static bool
is_acceptable(const Error error) is_acceptable(const Error error)
{ {
// TODO FIXME // Accept the error if the relative error is < max_ulp_error ulps.
const AcReal range = error.maximum_magnitude - error.minimum_magnitude; // Also consider the error zero if it is less than the minimum value in the mesh scaled to
if (error.abs_error < range * AC_REAL_EPSILON) // machine epsilon
const long double max_ulp_error = 5;
if (error.ulp_error < max_ulp_error)
return true;
else if (error.abs_error < error.minimum_magnitude * AC_REAL_EPSILON)
return true; return true;
else else
return false; return false;
} }
static void static bool
print_error_to_screen(const Error error) print_error_to_screen(const Error error)
{ {
bool errors_found = false;
printf("\t%-15s... ", vtxbuf_names[error.handle]); printf("\t%-15s... ", vtxbuf_names[error.handle]);
if (is_acceptable(error)) { if (is_acceptable(error)) {
printf(GRN "OK! " RESET); printf(GRN "OK! " RESET);
} }
else { else {
printf(RED "FAIL! " RESET); printf(RED "FAIL! " RESET);
errors_found = true;
} }
fprintf(stdout, "| %.2Lg (abs), %.2Lg (ulps), %.2Lg (rel). Range: [%.2g, %.2g]\n", // fprintf(stdout, "| %.3Lg (abs), %.3Lg (ulps), %.3Lg (rel). Range: [%.3g, %.3g]\n", //
error.abs_error, error.ulp_error, error.rel_error, // error.abs_error, error.ulp_error, error.rel_error, //
(double)error.minimum_magnitude, (double)error.maximum_magnitude); (double)error.minimum_magnitude, (double)error.maximum_magnitude);
return errors_found;
} }
/** Returns true when successful, false if errors were found. */
bool bool
acVerifyMesh(const AcMesh model, const AcMesh candidate) acVerifyMesh(const AcMesh model, const AcMesh candidate)
{ {
printf("Errors at the point of the maximum absolute error:\n");
bool errors_found = false;
for (int i = 0; i < NUM_VTXBUF_HANDLES; ++i) { for (int i = 0; i < NUM_VTXBUF_HANDLES; ++i) {
Error field_error = get_max_abs_error(i, model, candidate); Error field_error = get_max_abs_error(i, model, candidate);
print_error_to_screen(field_error); errors_found |= print_error_to_screen(field_error);
} }
printf("WARNING: is_acceptable() not yet complete\n");
return true; printf("%s\n", errors_found ? "Failure. Found errors in one or more vertex buffers"
: "Success. No errors found.");
return !errors_found;
} }