/*
* Copyright (C) Bull S.A. 2001
* Copyright (c) International Business Machines Corp., 2001
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/******************************************************************************/
/* */
/* Dec-03-2001 Created: Jacky Malcles & Jean Noel Cordenner */
/* These tests are adapted from AIX float PVT tests. */
/* */
/******************************************************************************/
#include "tfloat.h"
#define SAFE_FREE(p) { if (p) { free(p); (p)=NULL; } }
/*
* allocates a buffer and read a file to it
* input parameters:
* fname: name of the file to read
* data: pointer where buffer addr. will be returned
*
* uses also external variable datadir to build file pathname
*
* returns:
* 0 in case of failure
* # of bytes read elsewhere
*/
static size_t read_file(char *fname, void **data)
{
struct stat bufstat;
char path[PATH_MAX];
size_t fsize;
void *buffer;
int fd;
int maxretries = 1;
(void)sprintf(path, "%s/%s", datadir, fname);
errno = 0;
while (stat(path, &bufstat)) {
if (errno == ETIMEDOUT || errno == EINTR || errno == 0) {
printf("Error stat'ing %s: %s\n",
path, strerror(errno));
pthread_testcancel();
/* retrying... */
if (maxretries--)
continue;
}
return (size_t) 0;
}
fsize = bufstat.st_size;
if (!fsize) {
errno = ENOENT;
return (size_t) 0;
}
while ((buffer = malloc(fsize)) == NULL) {
if (errno == EINTR || errno == 0) {
printf("Error malloc'ing: %s\n", strerror(errno));
pthread_testcancel();
/* retrying... */
if (maxretries--)
continue;
}
return (size_t) 0;
}
while ((fd = open(path, O_RDONLY)) < 0) {
if (errno == ETIMEDOUT || errno == EINTR || errno == 0) {
printf("Error opening %s: %s\n", path, strerror(errno));
pthread_testcancel();
/* retrying... */
if (maxretries--)
continue;
}
SAFE_FREE(buffer);
return (size_t) 0;
}
while (read(fd, buffer, fsize) != fsize) {
if (errno == ETIMEDOUT || errno == EINTR || errno == 0) {
printf("Error reading %s: %s\n", path, strerror(errno));
pthread_testcancel();
/* retrying... */
if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) 0) {
if (maxretries--)
continue;
}
}
(void)close(fd);
SAFE_FREE(buffer);
return (size_t) 0;
}
(void)close(fd);
*data = buffer;
return fsize;
}
/* this subroutine is used in compute_xxx functions to check results
and record errors if appropriate */
static void check_error(TH_DATA * th_data, double e, double r, int index)
{
double x;
int pe, pr, px;
static const char errtmplt[] =
"%s failed at index %d: OLD: %2.18e NEW: %2.18e DIFF: %2.18e\n";
x = fabs(r - e); /* diff expected/computed */
if (x > EPS) { /* error ? */
/* compute exponent parts */
(void)frexp(r, &pr); /* for computed */
(void)frexp(x, &px); /* for difference */
(void)frexp(e, &pe); /* for dexected */
if (abs(pe - px) < th_data->th_func.precision ||
abs(pr - px) < th_data->th_func.precision) {
/* not a rounding error */
++th_data->th_nerror;
/* record first error only ! */
if (th_data->th_result == 0) {
sprintf(th_data->detail_data,
errtmplt,
th_data->th_func.fident,
index, e, r, x);
th_data->th_result = 1;
}
}
}
}
/*
* these functions handle the various cases of computation
* they are called by pthread_code
*/
/* normal case: compares f(input data) to expected data */
static void compute_normal(TH_DATA * th_data, double *din, double *dex,
int index)
{
double d, r, e;
d = din[index];
e = dex[index];
r = (*(th_data->th_func.funct)) (d);
check_error(th_data, e, r, index);
}
/* atan2 and hypot case: compares f(sin(input data),cos(input data))
to expected data */
static void compute_atan2_hypot(TH_DATA * th_data, double *din, double *dex,
int index)
{
double d, r, e;
d = din[index];
e = dex[index];
r = (*(th_data->th_func.funct)) (sin(d), cos(d));
check_error(th_data, e, r, index);
}
/* modf case: compares integral and fractional parts to expected datas */
static void compute_modf(TH_DATA * th_data, double *din, double *dex,
double *dex2, int index)
{
static const char errtmplt1[] =
"%s failed at index %d: OLD integral part: %f NEW: %f\n";
double d, r, e;
double tmp;
d = din[index];
e = dex[index];
r = (*(th_data->th_func.funct)) (d, &tmp);
if (tmp != dex2[index]) { /* bad integral part! */
++th_data->th_nerror;
/* record first error only ! */
if (th_data->th_result == 0) {
sprintf(th_data->detail_data,
errtmplt1,
th_data->th_func.fident,
index, dex2[index], tmp);
th_data->th_result = 1;
}
return;
}
check_error(th_data, e, r, index);
}
/* fmod and pow case: compares f(input data, input data2) to expected data */
static void compute_fmod_pow(TH_DATA * th_data, double *din, double *dex,
double *dex2, int index)
{
double d, r, e;
d = din[index];
e = dex[index];
r = (*(th_data->th_func.funct)) (d, dex2[index]);
check_error(th_data, e, r, index);
}
/* frexp case: compares mantissa and exponent to expected datas */
/* lgamma case: compares result and signgam to expected datas */
static void compute_frexp_lgamma(TH_DATA * th_data, double *din, double *dex,
int *dex2, int index)
{
static const char errtmplt2[] =
"%s failed at index %d: OLD (exp. or sign): %d NEW: %d\n";
double d, r, e;
int tmp;
static const char xinf[8] = "lgamma";
d = din[index];
e = dex[index];
r = (*(th_data->th_func.funct)) (d, &tmp);
if (strcmp(th_data->th_func.fident, xinf) != 0) {
if (tmp != dex2[index]) { /* bad exponent! */
++th_data->th_nerror;
/* record first error only ! */
if (th_data->th_result == 0) {
sprintf(th_data->detail_data,
errtmplt2,
th_data->th_func.fident,
index, dex2[index], tmp);
th_data->th_result = 1;
}
return;
}
}
check_error(th_data, e, r, index);
}
/* ldexp case: compares f(input data, input data2) to expected data */
static void compute_ldexp(TH_DATA * th_data, double *din, double *dex,
int *din2, int index)
{
double d, r, e;
d = din[index];
e = dex[index];
r = (*(th_data->th_func.funct)) (d, din2[index]);
check_error(th_data, e, r, index);
}
/*
* Function which does the job, to be called as the
* "start routine" parameter of pthread_create subroutine.
* Uses the compute_xxx subroutines above.
*
* input parameters ("arg" parameter of pthread_create subroutine):
* pointer to a TH_DATA structure.
*
*/
void *thread_code(void *arg)
{
TH_DATA *th_data = (TH_DATA *) arg;
size_t fsize, fsize2, fsize3;
double *din, *dex, *dex2 = NULL;
int imax, index;
fsize = read_file(th_data->th_func.din_fname, (void **)&din);
if (fsize == (size_t) 0) {
sprintf(th_data->detail_data,
"FAIL: %s: reading %s, %s\n",
th_data->th_func.fident,
th_data->th_func.din_fname, strerror(errno));
th_data->th_result = 1;
SAFE_FREE(din);
pthread_exit((void *)1);
}
fsize2 = read_file(th_data->th_func.dex_fname, (void **)&dex);
if (fsize2 == (size_t) 0) {
sprintf(th_data->detail_data,
"FAIL: %s: reading %s, %s\n",
th_data->th_func.fident,
th_data->th_func.dex_fname, strerror(errno));
th_data->th_result = 1;
SAFE_FREE(din);
SAFE_FREE(dex);
pthread_exit((void *)1);
}
fsize3 = (size_t) 0;
switch (th_data->th_func.code_funct) {
case FUNC_MODF:
case FUNC_FMOD:
case FUNC_POW:
case FUNC_FREXP:
case FUNC_LDEXP:
case FUNC_GAM:
fsize3 = read_file(th_data->th_func.dex2_fname, (void **)&dex2);
if (fsize3 == (size_t) 0) {
sprintf(th_data->detail_data,
"FAIL: %s: reading %s, %s\n",
th_data->th_func.fident,
th_data->th_func.dex2_fname, strerror(errno));
th_data->th_result = 1;
SAFE_FREE(din);
SAFE_FREE(dex);
pthread_exit((void *)1);
}
}
switch (th_data->th_func.code_funct) {
case FUNC_NORMAL:
case FUNC_ATAN2:
case FUNC_HYPOT:
if (fsize2 != fsize)
goto file_size_error;
break;
case FUNC_MODF:
case FUNC_FMOD:
case FUNC_POW:
if (fsize2 != fsize || fsize3 != fsize)
goto file_size_error;
break;
case FUNC_FREXP:
case FUNC_LDEXP:
case FUNC_GAM:
if (fsize2 != fsize ||
(sizeof(double) / sizeof(int)) * fsize3 != fsize)
goto file_size_error;
break;
default:
file_size_error:
sprintf(th_data->detail_data,
"FAIL: %s: file sizes don't match\n",
th_data->th_func.fident);
th_data->th_result = 1;
SAFE_FREE(din);
SAFE_FREE(dex);
if (fsize3)
SAFE_FREE(dex2);
pthread_exit((void *)1);
}
imax = fsize / sizeof(double);
while (th_data->th_nloop <= num_loops) {
/* loop stopped by pthread_cancel */
for (index = th_data->th_num; index < imax; index += num_threads) { /* computation loop */
switch (th_data->th_func.code_funct) {
case FUNC_NORMAL:
compute_normal(th_data, din, dex, index);
break;
case FUNC_ATAN2:
case FUNC_HYPOT:
compute_atan2_hypot(th_data, din, dex, index);
break;
case FUNC_MODF:
compute_modf(th_data, din, dex, dex2, index);
break;
case FUNC_FMOD:
case FUNC_POW:
compute_fmod_pow(th_data,
din, dex, dex2, index);
break;
case FUNC_FREXP:
case FUNC_GAM:
compute_frexp_lgamma(th_data,
din, dex, (int *)dex2,
index);
break;
case FUNC_LDEXP:
compute_ldexp(th_data,
din, dex, (int *)dex2, index);
break;
default:
sprintf(th_data->detail_data,
"FAIL: %s: unexpected function type\n",
th_data->th_func.fident);
th_data->th_result = 1;
SAFE_FREE(din);
SAFE_FREE(dex);
if (fsize3)
SAFE_FREE(dex2);
pthread_exit((void *)1);
}
pthread_testcancel();
} /* end of computation loop */
++th_data->th_nloop;
} /* end of loop */
SAFE_FREE(din);
SAFE_FREE(dex);
if (fsize3)
SAFE_FREE(dex2);
pthread_exit(NULL);
}