I've noticed that busybox uses few functions from libm.
I've written some replacements for those functions. Right
now this is just the test program and I've not made the
effort of including them in Busybox. However I'd like to
have your opinion: would it be a good idea to include
them ?
They are neither very fast nor very precise (the
trigonometric functions are awful, up to 16 bits are
false) but libm is rather large and usually a user of
Busybox on an initrd or embedded platform does not
need neither speed nor excellent precision. Otherwise
it would always be possible to disable them anyway.
Quick and dirty way to have a look:
cc -Wall -O2 -o libmtest libmtest.c -lm && ./libmtest
for f in err*
do
tail -1 $f | sed "s/^/$f: /"|cut -f1-2
done
The program does 10000 random tests for each of the 7
functions included in the program (log,exp, pow, sin, cos,
tan, atan2) and saves them in a (sorted) file. The first
column is the log2() of the worst relative error of libmtest
function against the same function of libm (i.e. the number
of the first error bit in the mantissa) and the second field is
the value of the arg. that provided the error (the error files are
more complete -- for pow and atan2 there is a second
argument but the for loop above does not print it even though
it is present in the error file).
Thanks,
Loïc Grenié
/*
* Copyright Loïc Grenié, <[EMAIL PROTECTED]>, 2008
*
* You can use this file according to version 2 or 3 of the General Public
* Licence distributed by the
* Free Software Foundation, Inc,.
* 59 Temple Place, Suite 330
* Boston, MA 0211-1307
* USA
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#define COMPARE_SIZE 10000
#ifndef INFINITY
#define INFINITY HUGE_VAL
#endif
#ifndef NAN
#if BB_BIG_ENDIAN
static unsigned char _nan[4] = { 0x7F, 0xF0, 0, 0};
#else
static unsigned char _nan[4] = { 0, 0, 0xF0, 0x7F};
#endif
#define NAN (*(float *)&_nan)
#endif
typedef union {
unsigned char c[8];
unsigned short s[4];
double d;
} _internal_double_t;
/* Position of the high-order bits of the doubles (in the "short" array) */
#if BB_BIG_ENDIAN
#define _DOUBLE_BITS48 0
#else
#define _DOUBLE_BITS48 3
#endif
double
mylog(double _x)
{
_internal_double_t x = *(_internal_double_t *)&_x;
double t, t2, t2n = 1., y = 0;
int n, i;
if (_x == 0)
return -INFINITY;
if (_x < 0)
return NAN;
n = (x.s[_DOUBLE_BITS48] >> 4) - 1023;
x.s[_DOUBLE_BITS48] &= 0x000F;
x.s[_DOUBLE_BITS48] |= 1023 << 4;
if (x.d>M_SQRT2) {
x.d /= 2;
n++;
}
t = 1-2/(x.d+1); /* t = (x_mant-1)/(x_mant+1) */
t2 = t*t;
for (i = 1; i <= 21; i += 2) {
y += t2n/i;
t2n *= t2;
}
return n/M_LOG2E+2*t*y;
}
double
myexp(double x)
{
_internal_double_t res;
double y;
int inv = 0, n, i;
if (x == 0) /* Improbable but possible ! */
return 1;
if (x < 0) {
x = -x;
inv = 1;
}
/* Here x > 0 */
y = x*M_LOG2E;
n = (int)y;
if (n >= 1024)
return inv ? 0 : INFINITY;
if (y-n > .5)
n++;
x -= n/M_LOG2E;
for (y = 1, res.d = 1, i = 1; i < 15; i++) {
y *= x/i;
res.d += y;
}
if (inv) {
res.d = 1/res.d;
n = -n;
}
res.s[_DOUBLE_BITS48] += n*16;
return res.d;
}
double mypow(double x, double y) { return myexp(y*mylog(x)); }
double
mycos_or_sin(int iscos, double x)
{
int i;
int n = (int)(x/M_PI);
double x2, y, res;
x -= n*M_PI;
if (x < 0) {
x = -x;
if (!iscos)
n++;
}
/* 0 <= x < M_PI */
if (x == 0)
return n & 1 ? -1 : 1;
if (x == M_PI/2)
return 0;
if (x > M_PI/2) {
x -= M_PI;
n++;
}
/* -M_PI/2 < x <= M_PI/2 */
if (x < 0) {
x = -x;
if (!iscos)
n++;
}
/* 0 <= x <= M_PI/2 */
#if 0
if (x > M_PI/4) {
x = M_PI/2 - x;
iscos = !iscos;
}
/* 0 <= x <= M_PI/4 */
#endif
if (iscos) {
y = 1;
i = 2;
}
else {
y = x;
i = 3;
}
res = y;
x2 = -x*x;
for (; i <= 42; i += 2) {
y *= x2/(i*(i-1));
res += y;
}
return n & 1 ? -res : res;
}
double mycos(double x) { return mycos_or_sin(1 ,x); }
double mysin(double x) { return mycos_or_sin(0, x); }
double mytan(double x) { return mycos_or_sin(0, x)/mycos_or_sin(1, x); }
double
mysqrt(double _x)
{
_internal_double_t x = *(_internal_double_t *)&_x, res;
double y = 1, x3;
short expo;
int i;
if (_x == 1 || _x == 0)
return _x;
if (_x < 0)
return NAN;
expo = x.s[_DOUBLE_BITS48] >> 4;
expo -= 1023;
expo /= 2;
x.s[_DOUBLE_BITS48] -= expo << 5;
if (x.d > 8./5) {
expo++;
x.d /= 4;
}
else if (x.d < 2./5) {
expo--;
x.d *= 4;
}
x.d -= 1;
x3 = 3 * x.d;
res.d = 1;
for (i = 2; i <= 120; i += 2)
{
y *= x3/i - x.d;
res.d += y;
}
res.s[_DOUBLE_BITS48] += expo << 4;
return res.d;
}
double
myatan2(double y, double x)
{
int dbl, i;
double t, t2n, res, mt2;
if (y == 0) {
if (x == 0)
return NAN;
if (x < 0)
return -M_PI;
return M_PI;
}
if (x == 0) {
if (y > 0)
return M_PI/2;
return -M_PI/2;
}
t = y/x;
for (dbl = 0; t > M_SQRT2-1 || t < 1-M_SQRT2; dbl++)
t = (sqrt(1+t*t)-1)/t;
t2n = t;
mt2 = -t*t;
res = t;
for (i = 3; i <= 41; i += 2) {
t2n *= mt2;
res += t2n/i;
}
for (; dbl > 0; dbl--)
res *= 2;
if (x < 0) {
if (res < 0)
res = res + M_PI;
else
res = res - M_PI;
}
return res;
}
void
printerr(char *type, double _r1, double _r2)
{
int i;
_internal_double_t r1, r2;
r1.d = _r1;
r2.d = _r2;
printf("%s: ", type);
for (i = 7; i >= 0; i--)
printf("%02X", r2.c[i] ^ r1.c[i]);
printf("\n");
}
int
main()
{
double comp_log[4*COMPARE_SIZE];
double comp_exp[4*COMPARE_SIZE];
double comp_pow[5*COMPARE_SIZE];
double comp_sin[4*COMPARE_SIZE];
double comp_cos[4*COMPARE_SIZE];
double comp_tan[4*COMPARE_SIZE];
double comp_sqrt[4*COMPARE_SIZE];
double comp_atan2[5*COMPARE_SIZE];
int i;
long seed;
FILE *f;
f = fopen("/dev/random", "r");
fread(&seed, sizeof seed, 1, f);
fclose(f);
srand48(seed);
for (i = 0; i < COMPARE_SIZE; i++) {
comp_log[4*i] = drand48() * 1000;
comp_log[4*i+1] = mylog(comp_log[4*i]);
comp_log[4*i+2] = log(comp_log[4*i]);
comp_log[4*i+3] = log2(fabs((comp_log[4*i+2]-comp_log[4*i+1])/comp_log[4*i+2]));
}
f = fopen("_errlog", "w");
for (i = 0; i < COMPARE_SIZE; i++)
if (!isnan(comp_log[4*i+3]) && !isinf(comp_log[4*i+3]))
fprintf(f, "%.20g\t%.20g\t%.20g\t%.20g\n", comp_log[4*i+3], comp_log[4*i], comp_log[4*i+1], comp_log[4*i+2]);
system("sort -g _errlog > errlog; rm _errlog");
fclose(f);
for (i = 0; i < COMPARE_SIZE; i++) {
comp_exp[4*i] = drand48() * 100 - 50;
comp_exp[4*i+1] = myexp(comp_exp[4*i]);
comp_exp[4*i+2] = exp(comp_exp[4*i]);
comp_exp[4*i+3] = log2(fabs((comp_exp[4*i+2]-comp_exp[4*i+1])/comp_exp[4*i+2]));
}
f = fopen("_errexp", "w");
for (i = 0; i < COMPARE_SIZE; i++)
if (!isnan(comp_exp[4*i+3]) && !isinf(comp_exp[4*i+3]))
fprintf(f, "%.20g\t%.20g\t%.20g\t%.20g\n", comp_exp[4*i+3], comp_exp[4*i], comp_exp[4*i+1], comp_exp[4*i+2]);
system("sort -g _errexp > errexp; rm _errexp");
fclose(f);
for (i = 0; i < COMPARE_SIZE/4; i++) {
comp_sin[4*i] = drand48() * 10;
comp_sin[4*i+1] = mysin(comp_sin[4*i]);
comp_sin[4*i+2] = sin(comp_sin[4*i]);
comp_sin[4*i+3] = log2(fabs((comp_sin[4*i+2]-comp_sin[4*i+1])/comp_sin[4*i+2]));
}
for ( ; i < 2 * COMPARE_SIZE/4; i++) {
comp_sin[4*i] = M_PI/2 + drand48() / 2;
comp_sin[4*i+1] = mysin(comp_sin[4*i]);
comp_sin[4*i+2] = sin(comp_sin[4*i]);
comp_sin[4*i+3] = log2(fabs((comp_sin[4*i+2]-comp_sin[4*i+1])/comp_sin[4*i+2]));
}
for ( ; i < 4 * COMPARE_SIZE/4; i++) {
comp_sin[4*i] = M_PI/2 + drand48() / 2;
comp_sin[4*i+1] = mysin(comp_sin[4*i]);
comp_sin[4*i+2] = sin(comp_sin[4*i]);
comp_sin[4*i+3] = log2(fabs((comp_sin[4*i+2]-comp_sin[4*i+1])/comp_sin[4*i+2]));
}
for ( ; i < 4 * COMPARE_SIZE/4; i++) {
comp_sin[4*i] = 3*M_PI/2 + drand48() / 2;
comp_sin[4*i+1] = mysin(comp_sin[4*i]);
comp_sin[4*i+2] = sin(comp_sin[4*i]);
comp_sin[4*i+3] = log2(fabs((comp_sin[4*i+2]-comp_sin[4*i+1])/comp_sin[4*i+2]));
}
f = fopen("_errsin", "w");
for (i = 0; i < COMPARE_SIZE; i++)
if (!isnan(comp_sin[4*i+3]) && !isinf(comp_sin[4*i+3]))
fprintf(f, "%.20g\t%.20g\t%.20g\t%.20g\n", comp_sin[4*i+3], comp_sin[4*i], comp_sin[4*i+1], comp_sin[4*i+2]);
system("sort -g _errsin > errsin; rm _errsin");
fclose(f);
for (i = 0; i < COMPARE_SIZE/4; i++) {
comp_cos[4*i] = drand48() * 10;
comp_cos[4*i+1] = mycos(comp_cos[4*i]);
comp_cos[4*i+2] = cos(comp_cos[4*i]);
comp_cos[4*i+3] = log2(fabs((comp_cos[4*i+2]-comp_cos[4*i+1])/comp_cos[4*i+2]));
}
for ( ; i < 2 * COMPARE_SIZE/4; i++) {
comp_cos[4*i] = M_PI/2 + drand48() / 2;
comp_cos[4*i+1] = mycos(comp_cos[4*i]);
comp_cos[4*i+2] = cos(comp_cos[4*i]);
comp_cos[4*i+3] = log2(fabs((comp_cos[4*i+2]-comp_cos[4*i+1])/comp_cos[4*i+2]));
}
for ( ; i < 3 * COMPARE_SIZE/4; i++) {
comp_cos[4*i] = M_PI/2 + drand48() / 2;
comp_cos[4*i+1] = mycos(comp_cos[4*i]);
comp_cos[4*i+2] = cos(comp_cos[4*i]);
comp_cos[4*i+3] = log2(fabs((comp_cos[4*i+2]-comp_cos[4*i+1])/comp_cos[4*i+2]));
}
for ( ; i < 4 * COMPARE_SIZE/4; i++) {
comp_cos[4*i] = 3*M_PI/2 + drand48() / 2;
comp_cos[4*i+1] = mycos(comp_cos[4*i]);
comp_cos[4*i+2] = cos(comp_cos[4*i]);
comp_cos[4*i+3] = log2(fabs((comp_cos[4*i+2]-comp_cos[4*i+1])/comp_cos[4*i+2]));
}
f = fopen("_errcos", "w");
for (i = 0; i < COMPARE_SIZE; i++)
if (!isnan(comp_cos[4*i+3]) && !isinf(comp_cos[4*i+3]))
fprintf(f, "%.20g\t%.20g\t%.20g\t%.20g\n", comp_cos[4*i+3], comp_cos[4*i], comp_cos[4*i+1], comp_cos[4*i+2]);
system("sort -g _errcos > errcos; rm _errcos");
fclose(f);
for (i = 0; i < COMPARE_SIZE/4; i++) {
comp_tan[4*i] = drand48() * 10;
comp_tan[4*i+1] = mytan(comp_tan[4*i]);
comp_tan[4*i+2] = tan(comp_tan[4*i]);
comp_tan[4*i+3] = log2(fabs((comp_tan[4*i+2]-comp_tan[4*i+1])/comp_tan[4*i+2]));
}
for ( ; i < 2 * COMPARE_SIZE/4; i++) {
comp_tan[4*i] = M_PI/2 + drand48() / 2;
comp_tan[4*i+1] = mytan(comp_tan[4*i]);
comp_tan[4*i+2] = tan(comp_tan[4*i]);
comp_tan[4*i+3] = log2(fabs((comp_tan[4*i+2]-comp_tan[4*i+1])/comp_tan[4*i+2]));
}
for ( ; i < 3 * COMPARE_SIZE/4; i++) {
comp_tan[4*i] = M_PI/2 + drand48() / 2;
comp_tan[4*i+1] = mytan(comp_tan[4*i]);
comp_tan[4*i+2] = tan(comp_tan[4*i]);
comp_tan[4*i+3] = log2(fabs((comp_tan[4*i+2]-comp_tan[4*i+1])/comp_tan[4*i+2]));
}
for ( ; i < 4 * COMPARE_SIZE/4; i++) {
comp_tan[4*i] = 3*M_PI/2 + drand48() / 2;
comp_tan[4*i+1] = mytan(comp_tan[4*i]);
comp_tan[4*i+2] = tan(comp_tan[4*i]);
comp_tan[4*i+3] = log2(fabs((comp_tan[4*i+2]-comp_tan[4*i+1])/comp_tan[4*i+2]));
}
f = fopen("_errtan", "w");
for (i = 0; i < COMPARE_SIZE; i++)
if (!isnan(comp_tan[4*i+3]) && !isinf(comp_tan[4*i+3]))
fprintf(f, "%.20g\t%.20g\t%.20g\t%.20g\n", comp_tan[4*i+3], comp_tan[4*i], comp_tan[4*i+1], comp_tan[4*i+2]);
system("sort -g _errtan > errtan; rm _errtan");
fclose(f);
for (i = 0; i < COMPARE_SIZE; i++) {
comp_sqrt[4*i] = drand48() * 1000;
comp_sqrt[4*i+1] = mysqrt(comp_sqrt[4*i]);
comp_sqrt[4*i+2] = sqrt(comp_sqrt[4*i]);
comp_sqrt[4*i+3] = log2(fabs((comp_sqrt[4*i+2]-comp_sqrt[4*i+1])/comp_sqrt[4*i+2]));
}
f = fopen("_errsqrt", "w");
for (i = 0; i < COMPARE_SIZE; i++)
if (!isnan(comp_sqrt[4*i+3]) && !isinf(comp_sqrt[4*i+3]))
fprintf(f, "%.20g\t%.20g\t%.20g\t%.20g\n", comp_sqrt[4*i+3], comp_sqrt[4*i], comp_sqrt[4*i+1], comp_sqrt[4*i+2]);
system("sort -g _errsqrt > errsqrt; rm _errsqrt");
fclose(f);
for (i = 0; i < COMPARE_SIZE; i++) {
comp_pow[5*i] = drand48() * 50;
comp_pow[5*i+1] = drand48() * 100 - 50;
comp_pow[5*i+2] = mypow(comp_pow[5*i], comp_pow[5*i+1]);
comp_pow[5*i+3] = pow(comp_pow[5*i], comp_pow[5*i+1]);
comp_pow[5*i+4] = log2(fabs((comp_pow[5*i+3]-comp_pow[5*i+2])/comp_pow[5*i+3]));
}
f = fopen("_errpow", "w");
for (i = 0; i < COMPARE_SIZE; i++)
if (!isnan(comp_pow[5*i+4]) && !isinf(comp_pow[5*i+4]))
fprintf(f, "%.20g\t%.20g\t%.20g\t%.20g\t%.20g\n", comp_pow[5*i+4], comp_pow[5*i], comp_pow[5*i+1], comp_pow[5*i+2], comp_pow[5*i+3]);
system("sort -g _errpow > errpow; rm _errpow");
fclose(f);
for (i = 0; i < COMPARE_SIZE; i++) {
comp_atan2[5*i] = drand48() * 100 - 50;
comp_atan2[5*i+1] = drand48() * 100 - 50;
comp_atan2[5*i+2] = myatan2(comp_atan2[5*i], comp_atan2[5*i+1]);
comp_atan2[5*i+3] = atan2(comp_atan2[5*i], comp_atan2[5*i+1]);
comp_atan2[5*i+4] = log2(fabs((comp_atan2[5*i+3]-comp_atan2[5*i+2])/comp_atan2[5*i+3]));
}
f = fopen("_erratan2", "w");
for (i = 0; i < COMPARE_SIZE; i++) {
if (!isnan(comp_atan2[5*i+4]) && !isinf(comp_atan2[5*i+4]))
fprintf(f, "%.20g\t%.20g\t%.20g\t%.20g\t%.20g\n", comp_atan2[5*i+4], comp_atan2[5*i], comp_atan2[5*i+1], comp_atan2[5*i+2], comp_atan2[5*i+3]);
}
system("sort -g _erratan2 > erratan2; rm _erratan2");
fclose(f);
exit(0);
}
_______________________________________________
busybox mailing list
[email protected]
http://busybox.net/cgi-bin/mailman/listinfo/busybox