blob: 3c465b8f0eb012eee2cfbb419c8a5a80322533c9 [file] [log] [blame]
/*
* Copyright (c) 2023 Lawrence King
*
* SPDX-License-Identifier: Apache-2.0
*
*/
#include <math.h>
#include <zephyr/kernel.h>
#include <zephyr/ztest.h>
#define local_abs(x) (((x) < 0) ? -(x) : (x))
#ifndef NAN
#define NAN (__builtin_nansf(""))
#endif
#ifndef INFINITY
#define INFINITY (__builtin_inff())
#endif
static float test_floats[] = {
1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f, 9.0f, /* numbers across the decade */
3.14159265359f, 2.718281828f, /* irrational numbers pi and e */
123.4f, 0.025f, 0.10f, 1.875f /* numbers with infinite */
/* repeating binary representation */
};
#define NUM_TEST_FLOATS (sizeof(test_floats)/sizeof(float))
static double test_doubles[] = {
1.0, 2.0, 3.0, 4.0,
5.0, 6.0, 7.0, 8.0, 9.0, /* numbers across the decade */
3.14159265359, 2.718281828, /* irrational numbers pi and e */
123.4, 0.025, 0.10, 1.875 /* numbers with infinite */
/* repeating binary representationa */
};
#define NUM_TEST_DOUBLES (sizeof(test_floats)/sizeof(float))
#ifndef isinf
static int isinf(double x)
{
union { uint64_t u; double d; } ieee754;
ieee754.d = x;
ieee754.u &= ~0x8000000000000000; /* ignore the sign */
return ((ieee754.u >> 52) == 0x7FF) &&
((ieee754.u & 0x000fffffffffffff) == 0);
}
#endif
#ifndef isnan
static int isnan(double x)
{
union { uint64_t u; double d; } ieee754;
ieee754.d = x;
ieee754.u &= ~0x8000000000000000; /* ignore the sign */
return ((ieee754.u >> 52) == 0x7FF) &&
((ieee754.u & 0x000fffffffffffff) != 0);
}
#endif
#ifndef isinff
static int isinff(float x)
{
union { uint32_t u; float f; } ieee754;
ieee754.f = x;
ieee754.u &= ~0x80000000; /* ignore the sign */
return ((ieee754.u >> 23) == 0xFF) &&
((ieee754.u & 0x7FFFFF) == 0);
}
#endif
#ifndef isnanf
static int isnanf(float x)
{
union { uint32_t u; float f; } ieee754;
ieee754.f = x;
ieee754.u &= ~0x80000000; /* ignore the sign */
return ((ieee754.u >> 23) == 0xFF) &&
((ieee754.u & 0x7FFFFF) != 0);
}
#endif
/* small errors are expected, computed as percentage error */
#define MAX_FLOAT_ERROR_PERCENT (3.5e-5f)
#define MAX_DOUBLE_ERROR_PERCENT (4.5e-14)
ZTEST(libc_common, test_sqrtf)
{
int i;
float exponent, resf, square, root_squared, error;
uint32_t max_error;
int32_t ierror;
int32_t *p_square = (int32_t *)&square;
int32_t *p_root_squared = (int32_t *)&root_squared;
max_error = 0;
/* test the special cases of 0.0, NAN, -NAN, INFINITY, -INFINITY, and -10.0 */
zassert_true(sqrtf(0.0f) == 0.0f, "sqrtf(0.0)");
zassert_true(isnanf(sqrtf(NAN)), "sqrt(nan)");
#ifdef issignallingf
zassert_true(issignallingf(sqrtf(NAN)), "ssignalingf(sqrtf(nan))");
/* printf("issignallingf();\n"); */
#endif
zassert_true(isnanf(sqrtf(-NAN)), "isnanf(sqrtf(-nan))");
zassert_true(isinff(sqrtf(INFINITY)), "isinff(sqrt(inf))");
zassert_true(isnanf(sqrtf(-INFINITY)), "isnanf(sqrt(-inf))");
zassert_true(isnanf(sqrtf(-10.0f)), "isnanf(sqrt(-10.0))");
for (exponent = 1.0e-10f; exponent < 1.0e10f; exponent *= 10.0f) {
for (i = 0; i < NUM_TEST_FLOATS; i++) {
square = test_floats[i] * exponent;
resf = sqrtf(square);
root_squared = resf * resf;
zassert_true((resf > 0.0f) && (resf < INFINITY),
"sqrtf out of range");
if ((resf > 0.0f) && (resf < INFINITY)) {
error = (square - root_squared) /
square * 100;
if (error < 0.0f) {
error = -error;
}
/* square and root_squared should be almost identical
* except the last few bits, the EXOR will only set
* the bits that are different
*/
ierror = (*p_square - *p_root_squared);
ierror = local_abs(ierror);
if (ierror > max_error) {
max_error = ierror;
}
} else {
/* negative, +NaN, -NaN, inf or -inf */
error = 0.0f;
}
zassert_true(error < MAX_FLOAT_ERROR_PERCENT,
"max sqrtf error exceeded");
}
}
zassert_true(max_error < 0x03, "huge errors in sqrt implementation");
/* print the max error */
TC_PRINT("test_sqrtf max error %d counts\n", max_error);
}
ZTEST(libc_common, test_sqrt)
{
int i;
double resd, error, square, root_squared, exponent;
uint64_t max_error;
int64_t ierror;
int64_t *p_square = (int64_t *)&square;
int64_t *p_root_squared = (int64_t *)&root_squared;
max_error = 0;
/* test the special cases of 0.0, NAN, -NAN, INFINITY, -INFINITY, and -10.0 */
zassert_true(sqrt(0.0) == 0.0, "sqrt(0.0)");
zassert_true(isnan(sqrt((double)NAN)), "sqrt(nan)");
#ifdef issignalling
zassert_true(issignalling(sqrt((double)NAN)), "ssignaling(sqrt(nan))");
/* printf("issignalling();\n"); */
#endif
zassert_true(isnan(sqrt((double)-NAN)), "isnan(sqrt(-nan))");
zassert_true(isinf(sqrt((double)INFINITY)), "isinf(sqrt(inf))");
zassert_true(isnan(sqrt((double)-INFINITY)), "isnan(sqrt(-inf))");
zassert_true(isnan(sqrt(-10.0)), "isnan(sqrt(-10.0))");
for (exponent = 1.0e-10; exponent < 1.0e10; exponent *= 10.0) {
for (i = 0; i < NUM_TEST_DOUBLES; i++) {
square = test_doubles[i] * exponent;
resd = sqrt(square);
root_squared = resd * resd;
zassert_true((resd > 0.0) && (resd < (double)INFINITY),
"sqrt out of range");
if ((resd > 0.0) && (resd < (double)INFINITY)) {
error = (square - root_squared) /
square * 100;
if (error < 0.0) {
error = -error;
}
/* square and root_squared should be almost identical
* except the last few bits, the EXOR will only set
* the bits that are different
*/
ierror = (*p_square - *p_root_squared);
ierror = local_abs(ierror);
if (ierror > max_error) {
max_error = ierror;
}
} else {
/* negative, +NaN, -NaN, inf or -inf */
error = 0.0;
}
zassert_true(error < MAX_DOUBLE_ERROR_PERCENT,
"max sqrt error exceeded");
}
}
zassert_true(max_error < 0x04, "huge errors in sqrt implementation");
/* print the max error */
TC_PRINT("test_sqrt max error %d counts\n", (uint32_t)max_error);
}