mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-10 21:47:06 +00:00
There are two copies of the round test in none/tests/ppc32/round.c and none/tests/ppc64/round.c. The two source files should be identical. The LE functional test commit updated the round.c test for ppc64 but was missing the ppc32 round updates. The round.c test was updated to fix an issue where we were getting different outputs depending on the compiler. The output is now consistent for the compilers allowing the removal of the additional expect files for ppc32 and ppc64. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@14278
1233 lines
30 KiB
C
1233 lines
30 KiB
C
|
|
/* Copyright (C) 2006 Dave Nomura
|
|
dcnltc@us.ibm.com
|
|
|
|
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., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
|
|
The GNU General Public License is contained in the file COPYING.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
|
|
typedef enum { FALSE=0, TRUE } bool_t;
|
|
|
|
typedef enum {
|
|
FADDS, FSUBS, FMULS, FDIVS,
|
|
FMADDS, FMSUBS, FNMADDS, FNMSUBS,
|
|
FADD, FSUB, FMUL, FDIV, FMADD,
|
|
FMSUB, FNMADD, FNMSUB, FSQRT
|
|
} flt_op_t;
|
|
|
|
typedef enum {
|
|
TO_NEAREST=0, TO_ZERO, TO_PLUS_INFINITY, TO_MINUS_INFINITY } round_mode_t;
|
|
char *round_mode_name[] = { "near", "zero", "+inf", "-inf" };
|
|
|
|
const char *flt_op_names[] = {
|
|
"fadds", "fsubs", "fmuls", "fdivs",
|
|
"fmadds", "fmsubs", "fnmadds", "fnmsubs",
|
|
"fadd", "fsub", "fmul", "fdiv", "fmadd", "fmsub", "fnmadd",
|
|
"fnmsub", "fsqrt"
|
|
};
|
|
|
|
typedef unsigned int fpscr_t;
|
|
|
|
typedef union {
|
|
float flt;
|
|
struct {
|
|
#if defined(VGP_ppc64le_linux)
|
|
unsigned int frac:23;
|
|
unsigned int exp:8;
|
|
unsigned int sign:1;
|
|
#else
|
|
unsigned int sign:1;
|
|
unsigned int exp:8;
|
|
unsigned int frac:23;
|
|
#endif
|
|
} layout;
|
|
} flt_overlay;
|
|
|
|
typedef union {
|
|
double dbl;
|
|
struct {
|
|
#if defined(VGP_ppc64le_linux)
|
|
unsigned int frac_lo:32;
|
|
unsigned int frac_hi:20;
|
|
unsigned int exp:11;
|
|
unsigned int sign:1;
|
|
#else
|
|
unsigned int sign:1;
|
|
unsigned int exp:11;
|
|
unsigned int frac_hi:20;
|
|
unsigned int frac_lo:32;
|
|
#endif
|
|
} layout;
|
|
struct {
|
|
unsigned int hi;
|
|
unsigned int lo;
|
|
} dbl_pair;
|
|
} dbl_overlay;
|
|
|
|
void assert_fail(const char *msg,
|
|
const char* expr, const char* file, int line, const char*fn);
|
|
|
|
#define STRING(__str) #__str
|
|
#define assert(msg, expr) \
|
|
((void) ((expr) ? 0 : \
|
|
(assert_fail (msg, STRING(expr), \
|
|
__FILE__, __LINE__, \
|
|
__PRETTY_FUNCTION__), 0)))
|
|
float denorm_small;
|
|
double dbl_denorm_small;
|
|
float norm_small;
|
|
bool_t debug = FALSE;
|
|
bool_t long_is_64_bits = sizeof(long) == 8;
|
|
|
|
void assert_fail (msg, expr, file, line, fn)
|
|
const char* msg;
|
|
const char* expr;
|
|
const char* file;
|
|
int line;
|
|
const char*fn;
|
|
{
|
|
printf( "\n%s: %s:%d (%s): Assertion `%s' failed.\n",
|
|
msg, file, line, fn, expr );
|
|
exit( 1 );
|
|
}
|
|
void set_rounding_mode(round_mode_t mode)
|
|
{
|
|
switch(mode) {
|
|
case TO_NEAREST:
|
|
asm volatile("mtfsfi 7, 0");
|
|
break;
|
|
case TO_ZERO:
|
|
asm volatile("mtfsfi 7, 1");
|
|
break;
|
|
case TO_PLUS_INFINITY:
|
|
asm volatile("mtfsfi 7, 2");
|
|
break;
|
|
case TO_MINUS_INFINITY:
|
|
asm volatile("mtfsfi 7, 3");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void print_double(char *msg, double dbl)
|
|
{
|
|
dbl_overlay D;
|
|
D.dbl = dbl;
|
|
|
|
printf("%15s : dbl %-20a = %c(%4d, %05x%08x)\n",
|
|
msg, D.dbl, (D.layout.sign == 0 ? '+' : '-'),
|
|
D.layout.exp, D.layout.frac_hi, D.layout.frac_lo);
|
|
}
|
|
|
|
void print_single(char *msg, float *flt)
|
|
{
|
|
flt_overlay F;
|
|
F.flt = *flt;
|
|
|
|
/* NOTE: for the purposes of comparing the fraction of a single with
|
|
** a double left shift the .frac so that hex digits are grouped
|
|
** from left to right. this is necessary because the size of a
|
|
** single mantissa (23) bits is not a multiple of 4
|
|
*/
|
|
printf("%15s : flt %-20a = %c(%4d, %06x)\n",
|
|
msg, F.flt, (F.layout.sign == 0 ? '+' : '-'), F.layout.exp, F.layout.frac << 1);
|
|
}
|
|
|
|
int check_dbl_to_flt_round(round_mode_t mode, double dbl, float *expected)
|
|
{
|
|
int status = 0;
|
|
flt_overlay R, E;
|
|
char *result;
|
|
|
|
set_rounding_mode(mode);
|
|
|
|
E.flt = *expected;
|
|
R.flt = (float)dbl;
|
|
|
|
if ((R.layout.sign != E.layout.sign) ||
|
|
(R.layout.exp != E.layout.exp) ||
|
|
(R.layout.frac != E.layout.frac)) {
|
|
result = "FAILED";
|
|
status = 1;
|
|
} else {
|
|
result = "PASSED";
|
|
status = 0;
|
|
}
|
|
printf("%s:%s:(double)(%-20a) = %20a",
|
|
round_mode_name[mode], result, R.flt, dbl);
|
|
if (status) {
|
|
print_single("\n\texpected", &E.flt);
|
|
print_single("\n\trounded ", &R.flt);
|
|
}
|
|
putchar('\n');
|
|
return status;
|
|
}
|
|
|
|
int test_dbl_to_float_convert(char *msg, float *base)
|
|
{
|
|
int status = 0;
|
|
double half = (double)denorm_small/2;
|
|
double qtr = half/2;
|
|
double D_hi = (double)*base + half + qtr;
|
|
double D_lo = (double)*base + half - qtr;
|
|
float F_lo = *base;
|
|
float F_hi = F_lo + denorm_small;
|
|
|
|
|
|
/*
|
|
** .....+-----+-----+-----+-----+---....
|
|
** ^F_lo ^ ^ ^
|
|
** D_lo
|
|
** D_hi
|
|
** F_hi
|
|
** F_lo and F_hi are two consecutive single float model numbers
|
|
** denorm_small distance apart. D_lo and D_hi are two numbers
|
|
** within that range that are not representable as single floats
|
|
** and will be rounded to either F_lo or F_hi.
|
|
*/
|
|
printf("-------------------------- %s --------------------------\n", msg);
|
|
if (debug) {
|
|
print_double("D_lo", D_lo);
|
|
print_double("D_hi", D_hi);
|
|
print_single("F_lo", &F_lo);
|
|
print_single("F_hi", &F_hi);
|
|
}
|
|
|
|
/* round to nearest */
|
|
status |= check_dbl_to_flt_round(TO_NEAREST, D_hi, &F_hi);
|
|
status |= check_dbl_to_flt_round(TO_NEAREST, D_lo, &F_lo);
|
|
|
|
/* round to zero */
|
|
status |= check_dbl_to_flt_round(TO_ZERO, D_hi, (D_hi > 0 ? &F_lo : &F_hi));
|
|
status |= check_dbl_to_flt_round(TO_ZERO, D_lo, (D_hi > 0 ? &F_lo : &F_hi));
|
|
|
|
/* round to +inf */
|
|
status |= check_dbl_to_flt_round(TO_PLUS_INFINITY, D_hi, &F_hi);
|
|
status |= check_dbl_to_flt_round(TO_PLUS_INFINITY, D_lo, &F_hi);
|
|
|
|
/* round to -inf */
|
|
status |= check_dbl_to_flt_round(TO_MINUS_INFINITY, D_hi, &F_lo);
|
|
status |= check_dbl_to_flt_round(TO_MINUS_INFINITY, D_lo, &F_lo);
|
|
return status;
|
|
}
|
|
|
|
void
|
|
init()
|
|
{
|
|
flt_overlay F;
|
|
dbl_overlay D;
|
|
|
|
/* small is the smallest denormalized single float number */
|
|
F.layout.sign = 0;
|
|
F.layout.exp = 0;
|
|
F.layout.frac = 1;
|
|
denorm_small = F.flt; /* == 2^(-149) */
|
|
if (debug) {
|
|
print_single("float small", &F.flt);
|
|
}
|
|
|
|
D.layout.sign = 0;
|
|
D.layout.exp = 0;
|
|
D.layout.frac_hi = 0;
|
|
D.layout.frac_lo = 1;
|
|
dbl_denorm_small = D.dbl; /* == 2^(-1022) */
|
|
if (debug) {
|
|
print_double("double small", D.dbl);
|
|
}
|
|
|
|
/* n_small is the smallest normalized single precision float */
|
|
F.layout.exp = 1;
|
|
norm_small = F.flt;
|
|
}
|
|
|
|
int check_int_to_flt_round(round_mode_t mode, long L, float *expected)
|
|
{
|
|
int status = 0;
|
|
int I = L;
|
|
char *int_name = "int";
|
|
flt_overlay R, E;
|
|
char *result;
|
|
int iter;
|
|
|
|
set_rounding_mode(mode);
|
|
E.flt = *expected;
|
|
|
|
for (iter = 0; iter < 2; iter++) {
|
|
int stat = 0;
|
|
R.flt = (iter == 0 ? (float)I : (float)L);
|
|
|
|
if ((R.layout.sign != E.layout.sign) ||
|
|
(R.layout.exp != E.layout.exp) ||
|
|
(R.layout.frac != E.layout.frac)) {
|
|
result = "FAILED";
|
|
stat = 1;
|
|
} else {
|
|
result = "PASSED";
|
|
stat = 0;
|
|
}
|
|
printf("%s:%s:(float)(%4s)%9d = %11.1f",
|
|
round_mode_name[mode], result, int_name, I, R.flt);
|
|
if (stat) {
|
|
print_single("\n\texpected: %.1f ", &E.flt);
|
|
print_single("\n\trounded ", &R.flt);
|
|
}
|
|
putchar('\n');
|
|
status |= stat;
|
|
|
|
if (!long_is_64_bits) break;
|
|
int_name = "long";
|
|
}
|
|
return status;
|
|
}
|
|
|
|
int check_long_to_dbl_round(round_mode_t mode, long L, double *expected)
|
|
{
|
|
int status = 0;
|
|
dbl_overlay R, E;
|
|
char *result;
|
|
|
|
set_rounding_mode(mode);
|
|
E.dbl = *expected;
|
|
|
|
R.dbl = (double)L;
|
|
|
|
if ((R.layout.sign != E.layout.sign) ||
|
|
(R.layout.exp != E.layout.exp) ||
|
|
(R.layout.frac_lo != E.layout.frac_lo) ||
|
|
(R.layout.frac_hi != E.layout.frac_hi)) {
|
|
result = "FAILED";
|
|
status = 1;
|
|
} else {
|
|
result = "PASSED";
|
|
status = 0;
|
|
}
|
|
printf("%s:%s:(double)(%18ld) = %20.1f",
|
|
round_mode_name[mode], result, L, R.dbl);
|
|
if (status) {
|
|
printf("\n\texpected %.1f : ", E.dbl);
|
|
}
|
|
putchar('\n');
|
|
return status;
|
|
}
|
|
|
|
int test_int_to_float_convert(char *msg)
|
|
{
|
|
int status = 0;
|
|
int int24_hi = 0x03ff0fff;
|
|
int int24_lo = 0x03ff0ffd;
|
|
float pos_flt_lo = 67047420.0;
|
|
float pos_flt_hi = 67047424.0;
|
|
float neg_flt_lo = -67047420.0;
|
|
float neg_flt_hi = -67047424.0;
|
|
|
|
printf("-------------------------- %s --------------------------\n", msg);
|
|
status |= check_int_to_flt_round(TO_NEAREST, int24_lo, &pos_flt_lo);
|
|
status |= check_int_to_flt_round(TO_NEAREST, int24_hi, &pos_flt_hi);
|
|
status |= check_int_to_flt_round(TO_ZERO, int24_lo, &pos_flt_lo);
|
|
status |= check_int_to_flt_round(TO_ZERO, int24_hi, &pos_flt_lo);
|
|
status |= check_int_to_flt_round(TO_PLUS_INFINITY, int24_lo, &pos_flt_hi);
|
|
status |= check_int_to_flt_round(TO_PLUS_INFINITY, int24_hi, &pos_flt_hi);
|
|
status |= check_int_to_flt_round(TO_MINUS_INFINITY, int24_lo, &pos_flt_lo);
|
|
status |= check_int_to_flt_round(TO_MINUS_INFINITY, int24_hi, &pos_flt_lo);
|
|
|
|
status |= check_int_to_flt_round(TO_NEAREST, -int24_lo, &neg_flt_lo);
|
|
status |= check_int_to_flt_round(TO_NEAREST, -int24_hi, &neg_flt_hi);
|
|
status |= check_int_to_flt_round(TO_ZERO, -int24_lo, &neg_flt_lo);
|
|
status |= check_int_to_flt_round(TO_ZERO, -int24_hi, &neg_flt_lo);
|
|
status |= check_int_to_flt_round(TO_PLUS_INFINITY, -int24_lo, &neg_flt_lo);
|
|
status |= check_int_to_flt_round(TO_PLUS_INFINITY, -int24_hi, &neg_flt_lo);
|
|
status |= check_int_to_flt_round(TO_MINUS_INFINITY, -int24_lo, &neg_flt_hi);
|
|
status |= check_int_to_flt_round(TO_MINUS_INFINITY, -int24_hi, &neg_flt_hi);
|
|
return status;
|
|
}
|
|
|
|
#ifdef __powerpc64__
|
|
int test_long_to_double_convert(char *msg)
|
|
{
|
|
int status = 0;
|
|
long long55_hi = 0x07ff0ffffffffff;
|
|
long long55_lo = 0x07ff0fffffffffd;
|
|
double pos_dbl_lo = 36012304344547324.0;
|
|
double pos_dbl_hi = 36012304344547328.0;
|
|
double neg_dbl_lo = -36012304344547324.0;
|
|
double neg_dbl_hi = -36012304344547328.0;
|
|
|
|
printf("-------------------------- %s --------------------------\n", msg);
|
|
status |= check_long_to_dbl_round(TO_NEAREST, long55_lo, &pos_dbl_lo);
|
|
status |= check_long_to_dbl_round(TO_NEAREST, long55_hi, &pos_dbl_hi);
|
|
status |= check_long_to_dbl_round(TO_ZERO, long55_lo, &pos_dbl_lo);
|
|
status |= check_long_to_dbl_round(TO_ZERO, long55_hi, &pos_dbl_lo);
|
|
status |= check_long_to_dbl_round(TO_PLUS_INFINITY, long55_lo, &pos_dbl_hi);
|
|
status |= check_long_to_dbl_round(TO_PLUS_INFINITY, long55_hi, &pos_dbl_hi);
|
|
status |= check_long_to_dbl_round(TO_MINUS_INFINITY, long55_lo, &pos_dbl_lo);
|
|
status |= check_long_to_dbl_round(TO_MINUS_INFINITY, long55_hi, &pos_dbl_lo);
|
|
|
|
status |= check_long_to_dbl_round(TO_NEAREST, -long55_lo, &neg_dbl_lo);
|
|
status |= check_long_to_dbl_round(TO_NEAREST, -long55_hi, &neg_dbl_hi);
|
|
status |= check_long_to_dbl_round(TO_ZERO, -long55_lo, &neg_dbl_lo);
|
|
status |= check_long_to_dbl_round(TO_ZERO, -long55_hi, &neg_dbl_lo);
|
|
status |= check_long_to_dbl_round(TO_PLUS_INFINITY, -long55_lo, &neg_dbl_lo);
|
|
status |= check_long_to_dbl_round(TO_PLUS_INFINITY, -long55_hi, &neg_dbl_lo);
|
|
status |= check_long_to_dbl_round(TO_MINUS_INFINITY, -long55_lo, &neg_dbl_hi);
|
|
status |= check_long_to_dbl_round(TO_MINUS_INFINITY, -long55_hi, &neg_dbl_hi);
|
|
return status;
|
|
}
|
|
#endif
|
|
|
|
int check_single_arithmetic_op(flt_op_t op)
|
|
{
|
|
char *result;
|
|
int status = 0;
|
|
dbl_overlay R, E;
|
|
double qtr, half, fA, fB, fD;
|
|
round_mode_t mode;
|
|
int q, s;
|
|
bool_t two_args = TRUE;
|
|
float whole = denorm_small;
|
|
|
|
#define BINOP(op) \
|
|
__asm__ volatile( \
|
|
op" %0, %1, %2\n\t" \
|
|
: "=f"(fD) : "f"(fA) , "f"(fB));
|
|
#define UNOP(op) \
|
|
__asm__ volatile( \
|
|
op" %0, %1\n\t" \
|
|
: "=f"(fD) : "f"(fA));
|
|
|
|
half = (double)whole/2;
|
|
qtr = half/2;
|
|
|
|
if (debug) {
|
|
print_double("qtr", qtr);
|
|
print_double("whole", whole);
|
|
print_double("2*whole", 2*whole);
|
|
}
|
|
|
|
for (mode = TO_NEAREST; mode <= TO_MINUS_INFINITY; mode++)
|
|
for (s = -1; s < 2; s += 2)
|
|
for (q = 1; q < 4; q += 2) {
|
|
double expected;
|
|
double lo = s*whole;
|
|
double hi = s*2*whole;
|
|
|
|
switch(op) {
|
|
case FADDS:
|
|
fA = s*whole;
|
|
fB = s*q*qtr;
|
|
break;
|
|
case FSUBS:
|
|
fA = s*2*whole;
|
|
fB = s*(q == 1 ? 3 : 1)*qtr;
|
|
break;
|
|
case FMULS:
|
|
fA = 0.5;
|
|
fB = s*(4+q)*half;
|
|
break;
|
|
case FDIVS:
|
|
fA = s*(4+q)*half;
|
|
fB = 2.0;
|
|
break;
|
|
default:
|
|
assert("check_single_arithmetic_op: unexpected op",
|
|
FALSE);
|
|
break;
|
|
}
|
|
|
|
switch(mode) {
|
|
case TO_NEAREST:
|
|
expected = (q == 1 ? lo : hi);
|
|
break;
|
|
case TO_ZERO:
|
|
expected = lo;
|
|
break;
|
|
case TO_PLUS_INFINITY:
|
|
expected = (s == 1 ? hi : lo);
|
|
break;
|
|
case TO_MINUS_INFINITY:
|
|
expected = (s == 1 ? lo : hi);
|
|
break;
|
|
}
|
|
|
|
set_rounding_mode(mode);
|
|
|
|
/*
|
|
** do the double precision dual operation just for comparison
|
|
** when debugging
|
|
*/
|
|
switch(op) {
|
|
case FADDS:
|
|
BINOP("fadds");
|
|
R.dbl = fD;
|
|
BINOP("fadd");
|
|
break;
|
|
case FSUBS:
|
|
BINOP("fsubs");
|
|
R.dbl = fD;
|
|
BINOP("fsub");
|
|
break;
|
|
case FMULS:
|
|
BINOP("fmuls");
|
|
R.dbl = fD;
|
|
BINOP("fmul");
|
|
break;
|
|
case FDIVS:
|
|
BINOP("fdivs");
|
|
R.dbl = fD;
|
|
BINOP("fdiv");
|
|
break;
|
|
default:
|
|
assert("check_single_arithmetic_op: unexpected op",
|
|
FALSE);
|
|
break;
|
|
}
|
|
#undef UNOP
|
|
#undef BINOP
|
|
|
|
E.dbl = expected;
|
|
|
|
if ((R.layout.sign != E.layout.sign) ||
|
|
(R.layout.exp != E.layout.exp) ||
|
|
(R.layout.frac_lo != E.layout.frac_lo) ||
|
|
(R.layout.frac_hi != E.layout.frac_hi)) {
|
|
result = "FAILED";
|
|
status = 1;
|
|
} else {
|
|
result = "PASSED";
|
|
status = 0;
|
|
}
|
|
|
|
printf("%s:%s:%s(%-13a",
|
|
round_mode_name[mode], result, flt_op_names[op], fA);
|
|
if (two_args) printf(", %-13a", fB);
|
|
printf(") = %-13a", R.dbl);
|
|
if (status) printf("\n\texpected %a", E.dbl);
|
|
putchar('\n');
|
|
|
|
if (debug) {
|
|
print_double("hi", hi);
|
|
print_double("lo", lo);
|
|
print_double("expected", expected);
|
|
print_double("got", R.dbl);
|
|
print_double("double result", fD);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
int check_single_guarded_arithmetic_op(flt_op_t op)
|
|
{
|
|
typedef struct {
|
|
int num, den, frac;
|
|
} fdivs_t;
|
|
|
|
char *result;
|
|
int status = 0;
|
|
flt_overlay A, B, Z;
|
|
dbl_overlay Res, Exp;
|
|
double fA, fB, fC, fD;
|
|
round_mode_t mode;
|
|
int g, s;
|
|
int arg_count;
|
|
|
|
fdivs_t divs_guard_cases[16] = {
|
|
{ 105, 56, 0x700000 }, /* : 0 */
|
|
{ 100, 57, 0x608FB8 }, /* : 1 */
|
|
{ 000, 00, 0x000000 }, /* : X */
|
|
{ 100, 52, 0x762762 }, /* : 3 */
|
|
{ 000, 00, 0x000000 }, /* : X */
|
|
{ 100, 55, 0x68BA2E }, /* : 5 */
|
|
{ 000, 00, 0x000000 }, /* : X */
|
|
{ 100, 51, 0x7AFAFA }, /* : 7 */
|
|
{ 000, 00, 0x000000 }, /* : X */
|
|
{ 100, 56, 0x649249 }, /* : 9 */
|
|
{ 000, 00, 0x000000 }, /* : X */
|
|
{ 100, 54, 0x6D097B }, /* : B */
|
|
{ 000, 00, 0x000000 }, /* : X */
|
|
{ 100, 59, 0x58F2FB }, /* : D */
|
|
{ 000, 00, 0x000000 }, /* : X */
|
|
{ 101, 52, 0x789D89 } /* : F */
|
|
};
|
|
|
|
/* 0x1.00000 00000000p-3 */
|
|
/* set up the invariant fields of B, the arg to cause rounding */
|
|
B.flt = 0.0;
|
|
B.layout.exp = 124; /* -3 */
|
|
|
|
/* set up args so result is always Z = 1.200000000000<g>p+0 */
|
|
Z.flt = 1.0;
|
|
Z.layout.sign = 0;
|
|
|
|
#define TERNOP(op) \
|
|
arg_count = 3; \
|
|
__asm__ volatile( \
|
|
op" %0, %1, %2, %3\n\t" \
|
|
: "=f"(fD) : "f"(fA) , "f"(fB), "f"(fC));
|
|
#define BINOP(op) \
|
|
arg_count = 2; \
|
|
__asm__ volatile( \
|
|
op" %0, %1, %2\n\t" \
|
|
: "=f"(fD) : "f"(fA) , "f"(fB));
|
|
#define UNOP(op) \
|
|
arg_count = 1; \
|
|
__asm__ volatile( \
|
|
op" %0, %1\n\t" \
|
|
: "=f"(fD) : "f"(fA));
|
|
|
|
for (mode = TO_NEAREST; mode <= TO_MINUS_INFINITY; mode++)
|
|
for (s = -1; s < 2; s += 2)
|
|
for (g = 0; g < 16; g += 1) {
|
|
double lo, hi, expected;
|
|
int LSB;
|
|
int guard = 0;
|
|
int z_sign = s;
|
|
|
|
/*
|
|
** one argument will have exponent = 0 as will the result (by
|
|
** design) so choose the other argument with exponent -3 to
|
|
** force a 3 bit shift for scaling leaving us with 3 guard bits
|
|
** and the LSB bit at the bottom of the manitssa.
|
|
*/
|
|
switch(op) {
|
|
case FADDS:
|
|
/* 1p+0 + 1.00000<g>p-3 */
|
|
B.layout.frac = g;
|
|
|
|
fB = s*B.flt;
|
|
fA = s*1.0;
|
|
|
|
/* set up Z to be truncated result */
|
|
|
|
/* mask off LSB from resulting guard bits */
|
|
guard = g & 7;
|
|
|
|
Z.layout.frac = 0x100000 | (g >> 3);
|
|
break;
|
|
case FSUBS:
|
|
/* 1.200002p+0 - 1.000000000000<g>p-3 */
|
|
A.flt = 1.125;
|
|
/* add enough to avoid scaling of the result */
|
|
A.layout.frac |= 0x2;
|
|
fA = s*A.flt;
|
|
|
|
B.layout.frac = g;
|
|
fB = s*B.flt;
|
|
|
|
/* set up Z to be truncated result */
|
|
guard = (0x10-g);
|
|
Z.layout.frac = guard>>3;
|
|
|
|
/* mask off LSB from resulting guard bits */
|
|
guard &= 7;
|
|
break;
|
|
case FMULS:
|
|
/* 1 + g*2^-23 */
|
|
A.flt = 1.0;
|
|
A.layout.frac = g;
|
|
fA = s*A.flt;
|
|
fB = 1.125;
|
|
|
|
/* set up Z to be truncated result */
|
|
Z.flt = 1.0;
|
|
Z.layout.frac = 0x100000;
|
|
Z.layout.frac |= g + (g>>3);
|
|
guard = g & 7;
|
|
break;
|
|
case FDIVS:
|
|
/* g >> 3 == LSB, g & 7 == guard bits */
|
|
guard = g & 7;
|
|
if ((guard & 1) == 0) {
|
|
/* special case: guard bit X = 0 */
|
|
A.flt = denorm_small;
|
|
A.layout.frac = g;
|
|
fA = A.flt;
|
|
fB = s*8.0;
|
|
Z.flt = 0.0;
|
|
Z.layout.frac |= (g >> 3);
|
|
} else {
|
|
fA = s*divs_guard_cases[g].num;
|
|
fB = divs_guard_cases[g].den;
|
|
|
|
Z.flt = 1.0;
|
|
Z.layout.frac = divs_guard_cases[g].frac;
|
|
}
|
|
break;
|
|
case FMADDS:
|
|
case FMSUBS:
|
|
case FNMADDS:
|
|
case FNMSUBS:
|
|
/* 1 + g*2^-23 */
|
|
A.flt = 1.0;
|
|
A.layout.frac = g;
|
|
fA = s*A.flt;
|
|
fB = 1.125;
|
|
|
|
/* 1.000001p-1 */
|
|
A.flt = 0.5;
|
|
A.layout.frac = 1;
|
|
fC = (op == FMADDS || op == FNMADDS ? s : -s)*A.flt;
|
|
|
|
/* set up Z to be truncated result */
|
|
z_sign = (op == FNMADDS || op == FNMSUBS ? -s : s);
|
|
guard = ((g & 7) + 0x4) & 7;
|
|
Z.flt = 1.0;
|
|
Z.layout.frac = 0x500000;
|
|
Z.layout.frac |= g + (g>>3) + ((g & 7)>> 2 ? 1 : 0);
|
|
break;
|
|
default:
|
|
assert("check_single_arithmetic_op: unexpected op",
|
|
FALSE);
|
|
break;
|
|
}
|
|
|
|
/* get LSB for tie breaking */
|
|
LSB = Z.layout.frac & 1;
|
|
|
|
/* set up hi and lo */
|
|
lo = z_sign*Z.flt;
|
|
Z.layout.frac += 1;
|
|
hi = z_sign*Z.flt;
|
|
|
|
switch(mode) {
|
|
case TO_NEAREST:
|
|
/* look at 3 guard bits to determine expected rounding */
|
|
switch(guard) {
|
|
case 0:
|
|
case 1: case 2: case 3:
|
|
expected = lo;
|
|
break;
|
|
case 4: /* tie: round to even */
|
|
if (debug) printf("tie: LSB = %d\n", LSB);
|
|
expected = (LSB == 0 ? lo : hi);
|
|
break;
|
|
case 5: case 6: case 7:
|
|
expected = hi;
|
|
break;
|
|
default:
|
|
assert("check_single_guarded_arithmetic_op: unexpected guard",
|
|
FALSE);
|
|
}
|
|
break;
|
|
case TO_ZERO:
|
|
expected = lo;
|
|
break;
|
|
case TO_PLUS_INFINITY:
|
|
if (guard == 0) {
|
|
/* no rounding */
|
|
expected = lo;
|
|
} else {
|
|
expected = (s == 1 ? hi : lo);
|
|
}
|
|
break;
|
|
case TO_MINUS_INFINITY:
|
|
if (guard == 0) {
|
|
/* no rounding */
|
|
expected = lo;
|
|
} else {
|
|
expected = (s == 1 ? lo : hi);
|
|
}
|
|
break;
|
|
}
|
|
|
|
set_rounding_mode(mode);
|
|
|
|
/*
|
|
** do the double precision dual operation just for comparison
|
|
** when debugging
|
|
*/
|
|
switch(op) {
|
|
case FADDS:
|
|
BINOP("fadds");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FSUBS:
|
|
BINOP("fsubs");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FMULS:
|
|
BINOP("fmuls");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FDIVS:
|
|
BINOP("fdivs");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FMADDS:
|
|
TERNOP("fmadds");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FMSUBS:
|
|
TERNOP("fmsubs");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FNMADDS:
|
|
TERNOP("fnmadds");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FNMSUBS:
|
|
TERNOP("fnmsubs");
|
|
Res.dbl = fD;
|
|
break;
|
|
default:
|
|
assert("check_single_guarded_arithmetic_op: unexpected op",
|
|
FALSE);
|
|
break;
|
|
}
|
|
#undef UNOP
|
|
#undef BINOP
|
|
#undef TERNOP
|
|
|
|
Exp.dbl = expected;
|
|
|
|
if ((Res.layout.sign != Exp.layout.sign) ||
|
|
(Res.layout.exp != Exp.layout.exp) ||
|
|
(Res.layout.frac_lo != Exp.layout.frac_lo) ||
|
|
(Res.layout.frac_hi != Exp.layout.frac_hi)) {
|
|
result = "FAILED";
|
|
status = 1;
|
|
} else {
|
|
result = "PASSED";
|
|
status = 0;
|
|
}
|
|
|
|
/* There seems to be some noise in the lower bits. The value
|
|
* on the least significant digit seems to vary when printing
|
|
* based on the rounding mode of the compiler. Just trying
|
|
* to get rid of the noise in the least significant bits when
|
|
* printing the operand.
|
|
*/
|
|
|
|
fA = ((long int)(fA*10000))/10000.0;
|
|
/* Change -0.0 to a positive 0.0. Some compilers print -0.0
|
|
* others do not. Make it consistent.
|
|
*/
|
|
if (fA == -0.0)
|
|
fA = 0.0;
|
|
|
|
printf("%s:%s:%s(%-13.6f",
|
|
round_mode_name[mode], result, flt_op_names[op], fA);
|
|
if (arg_count > 1) printf(", %-13a", fB);
|
|
if (arg_count > 2) printf(", %-13a", fC);
|
|
printf(") = %-13a", Res.dbl);
|
|
if (status) printf("\n\texpected %a", Exp.dbl);
|
|
putchar('\n');
|
|
|
|
if (debug) {
|
|
print_double("hi", hi);
|
|
print_double("lo", lo);
|
|
print_double("expected", expected);
|
|
print_double("got", Res.dbl);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
int check_double_guarded_arithmetic_op(flt_op_t op)
|
|
{
|
|
typedef struct {
|
|
int num, den, hi, lo;
|
|
} fdiv_t;
|
|
typedef struct {
|
|
double arg;
|
|
int exp, hi, lo;
|
|
} fsqrt_t;
|
|
|
|
char *result;
|
|
int status = 0;
|
|
dbl_overlay A, B, Z;
|
|
dbl_overlay Res, Exp;
|
|
double fA, fB, fC, fD;
|
|
round_mode_t mode;
|
|
int g, s;
|
|
int arg_count;
|
|
fdiv_t div_guard_cases[16] = {
|
|
{ 62, 62, 0x00000, 0x00000000 }, /* 0 */
|
|
{ 64, 62, 0x08421, 0x08421084 }, /* 1 */
|
|
{ 66, 62, 0x10842, 0x10842108 }, /* 2 */
|
|
{ 100, 62, 0x9ce73, 0x9ce739ce }, /* 3 */
|
|
{ 100, 62, 0x9ce73, 0x9ce739ce }, /* X */
|
|
{ 102, 62, 0xa5294, 0xa5294a52 }, /* 5 */
|
|
{ 106, 62, 0xb5ad6, 0xb5ad6b5a }, /* 6 */
|
|
{ 108, 62, 0xbdef7, 0xbdef7bde }, /* 7 */
|
|
{ 108, 108, 0x00000, 0x00000000 }, /* 8 */
|
|
{ 112, 62, 0xce739, 0xce739ce7 }, /* 9 */
|
|
{ 114, 62, 0xd6b5a, 0xd6b5ad6b }, /* A */
|
|
{ 116, 62, 0xdef7b, 0xdef7bdef }, /* B */
|
|
{ 84, 62, 0x5ad6b, 0x5ad6b5ad }, /* X */
|
|
{ 118, 62, 0xe739c, 0xe739ce73 }, /* D */
|
|
{ 90, 62, 0x739ce, 0x739ce739 }, /* E */
|
|
{ 92, 62, 0x7bdef, 0x7bdef7bd } /* F */
|
|
};
|
|
|
|
|
|
fsqrt_t sqrt_guard_cases[16] = {
|
|
{ 0x1.08800p0, 0, 0x04371, 0xd9ab72fb}, /* :0 B8.8440 */
|
|
{ 0x0.D2200p0, -1, 0xcfdca, 0xf353049e}, /* :1 A4.6910 */
|
|
{ 0x1.A8220p0, 0, 0x49830, 0x2b49cd6d}, /* :2 E9.D411 */
|
|
{ 0x1.05A20p0, 0, 0x02cd1, 0x3b44f3bf}, /* :3 B7.82D1 */
|
|
{ 0x0.CA820p0, -1, 0xc7607, 0x3cec0937}, /* :4 A1.6541 */
|
|
{ 0x1.DCA20p0, 0, 0x5d4f8, 0xd4e4c2b2}, /* :5 F7.EE51 */
|
|
{ 0x1.02C80p0, 0, 0x01630, 0x9cde7483}, /* :6 B6.8164 */
|
|
{ 0x0.DC800p0, -1, 0xdb2cf, 0xe686fe7c}, /* :7 A8.6E40 */
|
|
{ 0x0.CF920p0, -1, 0xcd089, 0xb6860626}, /* :8 A3.67C9 */
|
|
{ 0x1.1D020p0, 0, 0x0e1d6, 0x2e78ed9d}, /* :9 BF.8E81 */
|
|
{ 0x0.E1C80p0, -1, 0xe0d52, 0x6020fb6b}, /* :A AA.70E4 */
|
|
{ 0x0.C8000p0, -1, 0xc48c6, 0x001f0abf}, /* :B A0.6400 */
|
|
{ 0x1.48520p0, 0, 0x21e9e, 0xd813e2e2}, /* :C CD.A429 */
|
|
{ 0x0.F4C20p0, -1, 0xf4a1b, 0x09bbf0b0}, /* :D B1.7A61 */
|
|
{ 0x0.CD080p0, -1, 0xca348, 0x79b907ae}, /* :E A2.6684 */
|
|
{ 0x1.76B20p0, 0, 0x35b67, 0x81aed827} /* :F DB.BB59 */
|
|
};
|
|
|
|
/* 0x1.00000 00000000p-3 */
|
|
/* set up the invariant fields of B, the arg to cause rounding */
|
|
B.dbl = 0.0;
|
|
B.layout.exp = 1020;
|
|
|
|
/* set up args so result is always Z = 1.200000000000<g>p+0 */
|
|
Z.dbl = 1.0;
|
|
Z.layout.sign = 0;
|
|
|
|
#define TERNOP(op) \
|
|
arg_count = 3; \
|
|
__asm__ volatile( \
|
|
op" %0, %1, %2, %3\n\t" \
|
|
: "=f"(fD) : "f"(fA) , "f"(fB), "f"(fC));
|
|
#define BINOP(op) \
|
|
arg_count = 2; \
|
|
__asm__ volatile( \
|
|
op" %0, %1, %2\n\t" \
|
|
: "=f"(fD) : "f"(fA) , "f"(fB));
|
|
#define UNOP(op) \
|
|
arg_count = 1; \
|
|
__asm__ volatile( \
|
|
op" %0, %1\n\t" \
|
|
: "=f"(fD) : "f"(fA));
|
|
|
|
for (mode = TO_NEAREST; mode <= TO_MINUS_INFINITY; mode++)
|
|
for (s = (op != FSQRT ? -1 : 1); s < 2; s += 2)
|
|
for (g = 0; g < 16; g += 1) {
|
|
double lo, hi, expected;
|
|
int LSB;
|
|
int guard;
|
|
int z_sign = s;
|
|
|
|
/*
|
|
** one argument will have exponent = 0 as will the result (by
|
|
** design) so choose the other argument with exponent -3 to
|
|
** force a 3 bit shift for scaling leaving us with 3 guard bits
|
|
** and the LSB bit at the bottom of the manitssa.
|
|
*/
|
|
switch(op) {
|
|
case FADD:
|
|
/* 1p+0 + 1.000000000000<g>p-3 */
|
|
B.layout.frac_lo = g;
|
|
|
|
fB = s*B.dbl;
|
|
fA = s*1.0;
|
|
|
|
/* set up Z to be truncated result */
|
|
|
|
/* mask off LSB from resulting guard bits */
|
|
guard = g & 7;
|
|
|
|
Z.layout.frac_hi = 0x20000;
|
|
Z.layout.frac_lo = g >> 3;
|
|
|
|
break;
|
|
case FSUB:
|
|
/* 1.2000000000002p+0 - 1.000000000000<g>p-3 */
|
|
A.dbl = 1.125;
|
|
/* add enough to avoid scaling of the result */
|
|
A.layout.frac_lo = 0x2;
|
|
fA = s*A.dbl;
|
|
|
|
B.layout.frac_lo = g;
|
|
fB = s*B.dbl;
|
|
|
|
/* set up Z to be truncated result */
|
|
guard = (0x10-g);
|
|
Z.layout.frac_hi = 0x0;
|
|
Z.layout.frac_lo = guard>>3;
|
|
|
|
/* mask off LSB from resulting guard bits */
|
|
guard &= 7;
|
|
break;
|
|
case FMUL:
|
|
/* 1 + g*2^-52 */
|
|
A.dbl = 1.0;
|
|
A.layout.frac_lo = g;
|
|
fA = s*A.dbl;
|
|
fB = 1.125;
|
|
|
|
/* set up Z to be truncated result */
|
|
Z.dbl = 1.0;
|
|
Z.layout.frac_hi = 0x20000;
|
|
Z.layout.frac_lo = g + (g>>3);
|
|
guard = g & 7;
|
|
break;
|
|
case FMADD:
|
|
case FMSUB:
|
|
case FNMADD:
|
|
case FNMSUB:
|
|
/* 1 + g*2^-52 */
|
|
A.dbl = 1.0;
|
|
A.layout.frac_lo = g;
|
|
fA = s*A.dbl;
|
|
fB = 1.125;
|
|
|
|
/* 1.0000000000001p-1 */
|
|
A.dbl = 0.5;
|
|
A.layout.frac_lo = 1;
|
|
fC = (op == FMADD || op == FNMADD ? s : -s)*A.dbl;
|
|
|
|
/* set up Z to be truncated result */
|
|
z_sign = (op == FNMADD || op == FNMSUB ? -s : s);
|
|
guard = ((g & 7) + 0x4) & 7;
|
|
Z.dbl = 1.0;
|
|
Z.layout.frac_hi = 0xa0000;
|
|
Z.layout.frac_lo = g + (g>>3) + ((g & 7)>> 2 ? 1 : 0);
|
|
break;
|
|
case FDIV:
|
|
/* g >> 3 == LSB, g & 7 == guard bits */
|
|
guard = g & 7;
|
|
if (guard == 0x4) {
|
|
/* special case guard bits == 4, inexact tie */
|
|
fB = s*2.0;
|
|
Z.dbl = 0.0;
|
|
if (g >> 3) {
|
|
fA = dbl_denorm_small + 2*dbl_denorm_small;
|
|
Z.layout.frac_lo = 0x1;
|
|
} else {
|
|
fA = dbl_denorm_small;
|
|
}
|
|
} else {
|
|
fA = s*div_guard_cases[g].num;
|
|
fB = div_guard_cases[g].den;
|
|
|
|
printf("%d/%d\n",
|
|
s*div_guard_cases[g].num,
|
|
div_guard_cases[g].den);
|
|
Z.dbl = 1.0;
|
|
Z.layout.frac_hi = div_guard_cases[g].hi;
|
|
Z.layout.frac_lo = div_guard_cases[g].lo;
|
|
}
|
|
break;
|
|
case FSQRT:
|
|
fA = s*sqrt_guard_cases[g].arg;
|
|
Z.dbl = 1.0;
|
|
Z.layout.exp = sqrt_guard_cases[g].exp + 1023;
|
|
Z.layout.frac_hi = sqrt_guard_cases[g].hi;
|
|
Z.layout.frac_lo = sqrt_guard_cases[g].lo;
|
|
guard = g >> 1;
|
|
if (g & 1) guard |= 1;
|
|
/* don't have test cases for when X bit = 0 */
|
|
if (guard == 0 || guard == 4) continue;
|
|
break;
|
|
default:
|
|
assert("check_double_guarded_arithmetic_op: unexpected op",
|
|
FALSE);
|
|
break;
|
|
}
|
|
|
|
/* get LSB for tie breaking */
|
|
LSB = Z.layout.frac_lo & 1;
|
|
|
|
/* set up hi and lo */
|
|
lo = z_sign*Z.dbl;
|
|
Z.layout.frac_lo += 1;
|
|
hi = z_sign*Z.dbl;
|
|
|
|
switch(mode) {
|
|
case TO_NEAREST:
|
|
/* look at 3 guard bits to determine expected rounding */
|
|
switch(guard) {
|
|
case 0:
|
|
case 1: case 2: case 3:
|
|
expected = lo;
|
|
break;
|
|
case 4: /* tie: round to even */
|
|
if (debug) printf("tie: LSB = %d\n", LSB);
|
|
expected = (LSB == 0 ? lo : hi);
|
|
break;
|
|
case 5: case 6: case 7:
|
|
expected = hi;
|
|
break;
|
|
default:
|
|
assert("check_double_guarded_arithmetic_op: unexpected guard",
|
|
FALSE);
|
|
}
|
|
break;
|
|
case TO_ZERO:
|
|
expected = lo;
|
|
break;
|
|
case TO_PLUS_INFINITY:
|
|
if (guard == 0) {
|
|
/* no rounding */
|
|
expected = lo;
|
|
} else {
|
|
expected = (s == 1 ? hi : lo);
|
|
}
|
|
break;
|
|
case TO_MINUS_INFINITY:
|
|
if (guard == 0) {
|
|
/* no rounding */
|
|
expected = lo;
|
|
} else {
|
|
expected = (s == 1 ? lo : hi);
|
|
}
|
|
break;
|
|
}
|
|
|
|
set_rounding_mode(mode);
|
|
|
|
/*
|
|
** do the double precision dual operation just for comparison
|
|
** when debugging
|
|
*/
|
|
switch(op) {
|
|
case FADD:
|
|
BINOP("fadd");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FSUB:
|
|
BINOP("fsub");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FMUL:
|
|
BINOP("fmul");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FMADD:
|
|
TERNOP("fmadd");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FMSUB:
|
|
TERNOP("fmsub");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FNMADD:
|
|
TERNOP("fnmadd");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FNMSUB:
|
|
TERNOP("fnmsub");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FDIV:
|
|
BINOP("fdiv");
|
|
Res.dbl = fD;
|
|
break;
|
|
case FSQRT:
|
|
UNOP("fsqrt");
|
|
Res.dbl = fD;
|
|
break;
|
|
default:
|
|
assert("check_double_guarded_arithmetic_op: unexpected op",
|
|
FALSE);
|
|
break;
|
|
}
|
|
#undef UNOP
|
|
#undef BINOP
|
|
#undef TERNOP
|
|
|
|
Exp.dbl = expected;
|
|
|
|
if ((Res.layout.sign != Exp.layout.sign) ||
|
|
(Res.layout.exp != Exp.layout.exp) ||
|
|
(Res.layout.frac_lo != Exp.layout.frac_lo) ||
|
|
(Res.layout.frac_hi != Exp.layout.frac_hi)) {
|
|
result = "FAILED";
|
|
status = 1;
|
|
} else {
|
|
result = "PASSED";
|
|
status = 0;
|
|
}
|
|
|
|
printf("%s:%s:%s(%-13a",
|
|
round_mode_name[mode], result, flt_op_names[op], fA);
|
|
if (arg_count > 1) printf(", %-13a", fB);
|
|
if (arg_count > 2) printf(", %-13a", fC);
|
|
printf(") = %-13a", Res.dbl);
|
|
if (status) printf("\n\texpected %a", Exp.dbl);
|
|
putchar('\n');
|
|
|
|
if (debug) {
|
|
print_double("hi", hi);
|
|
print_double("lo", lo);
|
|
print_double("expected", expected);
|
|
print_double("got", Res.dbl);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
int test_float_arithmetic_ops()
|
|
{
|
|
int status = 0;
|
|
flt_op_t op;
|
|
|
|
/*
|
|
** choose FP operands whose result should be rounded to either
|
|
** lo or hi.
|
|
*/
|
|
|
|
printf("-------------------------- %s --------------------------\n",
|
|
"test rounding of float operators without guard bits");
|
|
for (op = FADDS; op <= FDIVS; op++) {
|
|
status |= check_single_arithmetic_op(op);
|
|
}
|
|
|
|
printf("-------------------------- %s --------------------------\n",
|
|
"test rounding of float operators with guard bits");
|
|
for (op = FADDS; op <= FNMSUBS; op++) {
|
|
status |= check_single_guarded_arithmetic_op(op);
|
|
}
|
|
|
|
printf("-------------------------- %s --------------------------\n",
|
|
"test rounding of double operators with guard bits");
|
|
for (op = FADD; op <= FSQRT; op++) {
|
|
status |= check_double_guarded_arithmetic_op(op);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
int
|
|
main()
|
|
{
|
|
int status = 0;
|
|
|
|
init();
|
|
|
|
status |= test_dbl_to_float_convert("test denormalized convert", &denorm_small);
|
|
status |= test_dbl_to_float_convert("test normalized convert", &norm_small);
|
|
status |= test_int_to_float_convert("test (float)int convert");
|
|
status |= test_int_to_float_convert("test (float)int convert");
|
|
|
|
#ifdef __powerpc64__
|
|
status |= test_long_to_double_convert("test (double)long convert");
|
|
#endif
|
|
status |= test_float_arithmetic_ops();
|
|
return status;
|
|
}
|