blob: dc09bb193de84847a91ddd1a21269cd7f447496a [file] [log] [blame]
/*
* Copyright (c) 2008-2010 Stefan Krah. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifdef _MSC_VER
#define llabs _abs64
#else
#define _XOPEN_SOURCE 600
#include <inttypes.h>
#endif
#include "mpdecimal.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "io.h"
#include "memory.h"
#include "mptest.h"
#include "mptypes.h"
#include "malloc_fail.h"
#define DECCHECK 1
#define DECNUMDIGITS 10000
#include "decNumber.h"
#define MAXOP DECNUMDIGITS
/* string operands */
static char cop1[MAXOP];
static char cop2[MAXOP];
static char cop3[MAXOP];
/* libmpdec contexts and operands */
static mpd_context_t mctx;
static mpd_context_t m_maxctx;
static mpd_t *mop1, *mop2, *mop3;
static mpd_t *mresult;
static mpd_t *mtmp;
/* decNumber contexts and operands */
static decContext dctx;
static decContext d_maxctx;
static decNumber dop1, dop2, dop3;
static decNumber dresult;
/* decNumber result strings */
static char dsci[DECNUMDIGITS+14];
static char deng[DECNUMDIGITS+14];
static mpd_ssize_t rt_pow10[] = {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
};
/* return value */
static int retval = 0;
static int exit_on_error = 0;
/* command line options */
static int verbose = 0;
static int short_tests = 0;
/* rudimentary statistics */
static uint64_t counter = 0;
static uint64_t nans = 0;
static uint64_t infinities = 0;
static uint64_t subnormals = 0;
static uint64_t zeros = 0;
static uint64_t skipped = 0;
static uint64_t skipped_flags = 0;
/* ========================================================================== */
/* Function IDs */
/* ========================================================================== */
enum {
/* Standard unary functions */
ABS, EXP, LN, LOG10, LOGB, MINUS, NEXTMINUS, NEXTPLUS, PLUS, REDUCE,
SQRT, TOINT, TOINTX,
/* Logical unary functions */
INVERT,
/* Char unary functions */
TOSCI, TOENG,
/* Const char unary functions */
CLASS,
/* Copy unary functions */
COPY, COPYABS, COPYNEG,
/* Standard binary functions */
ADD, DIV, NDIV, DIVINT, MAX, MAXMAG, MIN, MINMAG, MULTIPLY,
NEXTTWRD, POWER, QUANTIZE, REM, NREM, REMNEAR,
ROTATE, SCALEB, SHIFT, SUBTRACT,
/* Logical binary functions */
AND, OR, XOR,
/* Copy binary functions */
COPYSIGN,
/* Compare binary functions taking a context */
CMP, CMPSIG,
/* Compare binary functions */
CMPT, CMPTMAG,
/* Int binary functions */
SAMEQUANT,
/* Standard ternary functions */
FMA,
/* GUARD */
MAXID
};
enum {UN, BN, TN};
struct ts;
typedef struct ts {
const int id;
const char *name;
const int restricted;
const int type;
void (* testfunc)(const struct ts *);
const void *mfunc;
const void *dfunc;
} testset;
/* ========================================================================== */
/* Contexts */
/* ========================================================================== */
/* Restricted emax/emin (999999/-999999) for decNumber */
enum {NOT_RE, RE};
/* Min/Max Exponents */
#define IEEE_MAX 3
static mpd_ssize_t ieee_prec[] = { 7, 16, 34};
static mpd_ssize_t ieee_emax[] = { 96, 384, 6144};
static mpd_ssize_t ieee_emin[] = {-95, -383, -6143};
/* Rounding modes */
static int mround[] = {
MPD_ROUND_UP, MPD_ROUND_DOWN, MPD_ROUND_CEILING, MPD_ROUND_FLOOR,
MPD_ROUND_HALF_UP, MPD_ROUND_HALF_DOWN, MPD_ROUND_HALF_EVEN, MPD_ROUND_05UP
};
static int dround[] = {
DEC_ROUND_UP, DEC_ROUND_DOWN, DEC_ROUND_CEILING, DEC_ROUND_FLOOR,
DEC_ROUND_HALF_UP, DEC_ROUND_HALF_DOWN, DEC_ROUND_HALF_EVEN, DEC_ROUND_05UP
};
static void
mpd_readcontext(mpd_context_t *mctx, decContext *dctx)
{
mctx->prec=DECNUMDIGITS;
mctx->emax=999999999;
mctx->emin=-999999999;
mctx->round=MPD_ROUND_HALF_UP;
mctx->traps=MPD_Traps;
mctx->traps=0;
mctx->status=0;
mctx->newtrap=0;
mctx->clamp=0;
mctx->allcr=0;
dctx->digits=DECNUMDIGITS;
dctx->emax=999999999;
dctx->emin=-999999999;
dctx->round=DEC_ROUND_HALF_UP;
dctx->traps=DEC_NaNs;
dctx->traps=0;
dctx->status=0;
dctx->clamp=0;
}
static void
mpd_testcontext(mpd_context_t *mctx, decContext *dctx)
{
mctx->prec=DECNUMDIGITS;
mctx->emax=999999999;
mctx->emin=-999999999;
mctx->round=MPD_ROUND_HALF_UP;
mctx->traps=0;
mctx->status=0;
mctx->newtrap=0;
mctx->clamp=0;
mctx->allcr=0;
dctx->digits=DECNUMDIGITS;
dctx->emax=999999999;
dctx->emin=-999999999;
dctx->round=DEC_ROUND_HALF_UP;
dctx->traps=0;
dctx->status=0;
dctx->clamp=0;
}
static void
mpd_restrcontext(mpd_context_t *mctx, decContext *dctx)
{
mctx->emax=999999;
mctx->emin=-999999;
dctx->emax=999999;
dctx->emin=-999999;
}
/* ========================================================================== */
/* Print Errors */
/* ========================================================================== */
/* decNumber flags as a list */
void
decflags_as_list(char dest[], uint32_t status)
{
size_t len;
dest[0] = '[';
dest[1] = '\0';
if (status&DEC_Clamped) strcat(dest, "Clamped, ");
if (status&DEC_Conversion_syntax) strcat(dest, "Conversion_syntax, ");
if (status&DEC_Division_by_zero) strcat(dest, "Division_by_zero, ");
if (status&DEC_Division_impossible) strcat(dest, "Division_impossible, ");
if (status&DEC_Division_undefined) strcat(dest, "Division_undefined, ");
if (status&DEC_Insufficient_storage) strcat(dest, "Insufficient_storage, ");
if (status&DEC_Inexact) strcat(dest, "Inexact, ");
if (status&DEC_Invalid_context) strcat(dest, "Invalid_context, ");
if (status&DEC_Invalid_operation) strcat(dest, "Invalid_operation, ");
if (status&DEC_Overflow) strcat(dest, "Overflow, ");
if (status&DEC_Rounded) strcat(dest, "Rounded, ");
if (status&DEC_Subnormal) strcat(dest, "Subnormal, ");
if (status&DEC_Underflow) strcat(dest, "Underflow, ");
len = strlen(dest) - 1;
while(dest[len] == ' ' || dest[len] == ',')
len--;
dest[len+1] = ']';
dest[len+2] = '\0';
}
/* translate decNumber rounding constants to strings */
static const char *dec_round_string[] = {
"ROUND_CEILING",
"ROUND_UP",
"ROUND_HALF_UP",
"ROUND_HALF_EVEN",
"ROUND_HALF_DOWN",
"ROUND_DOWN",
"ROUND_FLOOR",
"ROUND_05UP",
"ROUND_MAX"
};
static void
print_contexts(void)
{
char m_traps_list[MPD_MAX_FLAG_LIST];
char m_status_list[MPD_MAX_FLAG_LIST];
char d_traps_list[MPD_MAX_FLAG_LIST];
char d_status_list[MPD_MAX_FLAG_LIST];
mpd_lsnprint_flags(m_traps_list, MPD_MAX_FLAG_LIST, mctx.traps, NULL);
mpd_lsnprint_flags(m_status_list, MPD_MAX_FLAG_LIST, mctx.status, NULL);
decflags_as_list(d_traps_list, dctx.traps);
decflags_as_list(d_status_list, dctx.status);
printf("mctx(prec=%"PRI_mpd_ssize_t", emax=%"PRI_mpd_ssize_t""
", emin=%"PRI_mpd_ssize_t", traps=%s, status=%s, "
"round=%s, clamp=%d)\n",
mctx.prec, mctx.emax, mctx.emin,
m_traps_list, m_status_list,
mpd_round_string[mctx.round],
mctx.clamp);
printf("dctx(prec=%d, emax=%d, emin=%d, traps=%s, status=%s, "
"round=%s, clamp=%d)\n\n",
dctx.digits, dctx.emax, dctx.emin,
d_traps_list, d_status_list,
dec_round_string[dctx.round],
dctx.clamp);
fflush(stdout);
}
static void
print_err(const testset *t, const char *msci, const char *dsci,
const char *meng, const char *deng)
{
switch (t->type) {
case UN:
printf("%s %s\n", t->name, cop1);
break;
case BN:
printf("%s %s %s\n", t->name, cop1, cop2);
break;
case TN:
printf("%s %s %s %s\n", t->name, cop1, cop2, cop3);
break;
default:
abort();
}
if (meng) {
printf("msci: %s meng: %s\n", msci, meng);
printf("dsci: %s deng: %s\n\n", dsci, deng);
}
else {
printf("msci: %s\n", msci);
printf("dsci: %s\n\n", dsci);
}
fflush(stdout);
print_contexts();
}
/* ========================================================================== */
/* Compare Results */
/* ========================================================================== */
static int
compare_contexts(void)
{
if (!!(mctx.status&MPD_Clamped) != !!(dctx.status&DEC_Clamped) ||
!!(mctx.status&MPD_Conversion_syntax) != !!(dctx.status&DEC_Conversion_syntax) ||
!!(mctx.status&MPD_Division_by_zero) != !!(dctx.status&DEC_Division_by_zero) ||
!!(mctx.status&MPD_Division_impossible) != !!(dctx.status&DEC_Division_impossible) ||
!!(mctx.status&MPD_Division_undefined) != !!(dctx.status&DEC_Division_undefined) ||
!!(mctx.status&MPD_Inexact) != !!(dctx.status&DEC_Inexact) ||
!!(mctx.status&MPD_Invalid_context) != !!(dctx.status&DEC_Invalid_context) ||
!!(mctx.status&MPD_Invalid_operation) != !!(dctx.status&DEC_Invalid_operation) ||
!!(mctx.status&MPD_Malloc_error) != !!(dctx.status&DEC_Insufficient_storage) ||
!!(mctx.status&MPD_Overflow) != !!(dctx.status&DEC_Overflow) ||
!!(mctx.status&MPD_Rounded) != !!(dctx.status&DEC_Rounded) ||
!!(mctx.status&MPD_Subnormal) != !!(dctx.status&DEC_Subnormal) ||
!!(mctx.status&MPD_Underflow) != !!(dctx.status&DEC_Underflow)) {
return 0;
}
return 1;
}
static void
harrison_ulp(mpd_t *ulp, mpd_t *dec)
{
mpd_context_t ctx;
mpd_t *a, *b;
ctx = mctx;
a = mpd_qnew();
b = mpd_qnew();
mpd_next_plus(a, dec, &ctx);
mpd_next_minus(b, dec, &ctx);
mpd_sub(ulp, a, b, &ctx);
mpd_abs(ulp, ulp, &ctx);
mpd_del(a);
mpd_del(b);
}
static void
standard_ulp(mpd_t *ulp, mpd_t *dec, mpd_ssize_t prec)
{
mpd_context_t ctx;
ctx = mctx;
mpd_set_uint(ulp, 1, &ctx);
ulp->exp = dec->exp + dec->digits - prec;
}
enum {LIBMPDEC, LIBDECNUMBER};
static int
check_ulpdiff(int lib, mpd_t *exact, mpd_t *rounded)
{
mpd_context_t maxcontext, ctx;
mpd_t *x, *y;
mpd_t *t, *ulp, *err;
const char *maxerr = "1.1";
int ret;
ctx = mctx;
mpd_maxcontext(&maxcontext);
maxcontext.traps = 0;
maxcontext.emax = 1000000000;
maxcontext.emin = -1000000000;
/* Convert infinities to the largest representable number + 1ULP. */
x = exact;
if (!mpd_iszero(exact) && mpd_adjexp(exact) > ctx.emax) {
/* If the exact result is in infinity territory, decNumber is
* sometimes off by several ulps from the exact result, but
* within one ulp from round(exact) = Infinity. */
x = mpd_qnew();
mpd_set_string(x, "10", &maxcontext);
x->exp = ctx.emax;
mpd_signcpy(x, exact);
}
y = rounded;
if (mpd_isinfinite(rounded)) {
y = mpd_qnew();
mpd_set_string(y, "10", &maxcontext);
y->exp = ctx.emax;
mpd_signcpy(y, exact);
}
t = mpd_qnew();
ulp = mpd_qnew();
err = mpd_qnew();
/* err = (rounded - exact) / ulp(rounded) */
maxcontext.prec = ctx.prec <= 5 ? 10 : 2*ctx.prec;
mpd_sub(t, y, x, &maxcontext);
if ((lib == LIBMPDEC &&
(mctx.status&MPD_Clamped ||
mctx.status&MPD_Underflow)) ||
(lib == LIBDECNUMBER &&
(dctx.status&DEC_Clamped ||
dctx.status&DEC_Underflow))) {
harrison_ulp(ulp, y);
}
else {
standard_ulp(ulp, y, ctx.prec);
}
mpd_div(err, t, ulp, &maxcontext);
mpd_abs(err, err, &maxcontext);
mpd_set_string(t, maxerr, &maxcontext);
ret = (mpd_cmp(err, t, &maxcontext) < 0) ? 1 : 0;
if (x != exact) mpd_del(x);
if (y != rounded) mpd_del(y);
mpd_del(t);
mpd_del(ulp);
mpd_del(err);
return ret;
}
static int
resolve_ulp(const testset *t, const char *dsci)
{
mpd_context_t maxcontext, ctx;
mpd_t *exact, *rounded;
int ret = 1;
ctx = mctx;
mpd_maxcontext(&maxcontext);
maxcontext.prec = ctx.prec <= 5 ? 10 : 2*ctx.prec;
maxcontext.traps = 0;
exact = mpd_qnew();
switch (t->id) {
case EXP:
mpd_exp(exact, mop1, &maxcontext);
break;
case LN:
mpd_ln(exact, mop1, &maxcontext);
break;
case LOG10:
mpd_log10(exact, mop1, &maxcontext);
break;
case POWER:
mpd_pow(exact, mop1, mop2, &maxcontext);
break;
/* decNumber: Issue 1: incorrect rounding in add/subtract/fma */
case ADD:
mpd_add(exact, mop1, mop2, &maxcontext);
break;
case SUBTRACT:
mpd_sub(exact, mop1, mop2, &maxcontext);
break;
case FMA:
mpd_fma(exact, mop1, mop2, mop3, &maxcontext);
break;
/* End Issue 1 */
default:
mpd_del(exact);
return 0;
}
/* test mpd_t result */
rounded = mresult;
if (!check_ulpdiff(LIBMPDEC, exact, rounded)) {
ret = 0;
}
/* test decNumber result */
rounded = mpd_qnew();
mpd_set_string(rounded, dsci, &maxcontext);
if (!check_ulpdiff(LIBDECNUMBER, exact, rounded)) {
ret = 0;
}
mpd_del(exact);
mpd_del(rounded);
return ret;
}
static void
compare_results(const testset *t)
{
char *msci;
char *meng;
int have_dot;
int skip_contexts = 0;
char *mp;
char *dp;
msci = mpd_to_sci(mresult, 1);
meng = mpd_to_eng(mresult, 1);
decNumberToString(&dresult, dsci);
decNumberToEngString(&dresult, deng);
/* NaN payload differences: skip for all functions */
if (mpd_isnan(mresult) && decNumberIsNaN(&dresult)) {
/* In IEEE 754 interchange formats a NaN payload has one
digit fewer. libmpdec applies this rule whenever 'clamp'
is set. */
if (mctx.clamp)
goto skip;
/* decNumber allows the first digit of the payload to be zero. */
if (strstr(dsci, "NaN0"))
goto skip;
/* decNumber: Issue 19: If the second operand is sNaN and the
third operand is outside the allowed range, decNumber generates
a NaN from the third operand, i.e. it does not use the payload
from the second operand. */
if (t->id == FMA) {
goto skip;
}
}
/* decNumber restrictions */
if (t->restricted && !(mctx.status&MPD_Invalid_operation) &&
dctx.status&DEC_Invalid_operation)
goto skip;
/* Function specific skips */
switch (t->id) {
case ADD: case FMA: case SUBTRACT:
/* decNumber: Issue 2: Overflow status in add/subtract */
/* decNumber: Issue 3: Clamped status in add/subtract */
if (mctx.round == MPD_ROUND_05UP ||
mctx.round == MPD_ROUND_DOWN ||
mctx.round == MPD_ROUND_CEILING ||
mctx.round == MPD_ROUND_FLOOR) {
if ((!!(mctx.status&MPD_Overflow) != !!(dctx.status&DEC_Overflow)) ||
((mctx.status&MPD_Clamped) && !(dctx.status&DEC_Clamped))) {
skip_contexts = 1;
}
}
/* decNumber: Issue 4: Subnormal/Underflow status in add/subtract */
if (!!(dctx.status&DEC_Subnormal) != !!(mctx.status&MPD_Subnormal) &&
!!(dctx.status&DEC_Underflow) != !!(mctx.status&MPD_Underflow)) {
skip_contexts = 1;
}
/* decNumber: Issue 5: Inexact status in add/subtract */
if (dctx.status == (DEC_Rounded|DEC_Inexact) &&
mctx.status == MPD_Rounded) {
skip_contexts = 1;
}
break;
case AND: case OR: case XOR:
/* decNumber: Issue 6: Logical functions with invalid excess digits */
if (mop1->digits > mctx.prec || mop2->digits > mctx.prec) {
if (mctx.status==MPD_Invalid_operation &&
dctx.status==0)
goto skip;
}
break;
case EXP:
/* decNumber: Flag-issue 1: Subnormal status in exp */
if (mctx.status==(MPD_Rounded|MPD_Inexact) &&
dctx.status==(DEC_Rounded|DEC_Inexact|DEC_Subnormal)) {
skip_contexts = 1;
}
break;
case INVERT:
/* decNumber: Issue 6: Logical functions with invalid excess digits */
if (mop1->digits > mctx.prec) {
if (mctx.status==MPD_Invalid_operation &&
dctx.status==0)
goto skip;
}
break;
case LN:
/* decNumber: Overflow not set in ln. This is a side effect
of the patch for Issue 20. */
if (((mctx.status&MPD_Overflow) && !(dctx.status&DEC_Overflow)) ||
((mctx.status&MPD_Subnormal) && !(dctx.status&DEC_Subnormal))) {
skip_contexts = 1;
}
break;
case LOG10:
/* decNumber: Issue 7: NaN payload exceeding precision */
if (mpd_isnan(mresult) && decNumberIsNaN(&dresult)) {
if (strlen(msci) != strlen(dsci))
goto skip;
}
/* decNumber: Overflow not set in ln. This is a side effect
of the patch for Issue 20. */
if (((mctx.status&MPD_Overflow) && !(dctx.status&DEC_Overflow)) ||
((mctx.status&MPD_Subnormal) && !(dctx.status&DEC_Subnormal))) {
skip_contexts = 1;
}
/* decNumber: Issue 18: no Overflow to -Infinity */
if (mpd_isnegative(mresult) && mpd_isinfinite(mresult) &&
!decNumberIsInfinite(&dresult)) {
goto skip;
}
break;
case NEXTMINUS: case NEXTPLUS:
/* decNumber: Issue 8: incorrect results in next_minus and next_plus */
if (mresult->exp == mpd_etiny(&mctx)) goto skip;
if (llabs(mop1->exp) > 999999999L) goto skip;
break;
case NEXTTWRD:
/* "In the first two cases, flags are set as though the operation had
been computed by adding (in the first case) or subtracting
(in the second) an infinitesimally small positive value to or from
the first operand with the rounding mode set to be round-ceiling or
round-floor respectively." */
if (mpd_iszero(mresult))
/* status??? */
goto skip;
if ((mctx.status&MPD_Inexact))
/* status??? */
goto skip;
/* decNumber: similar to issue 8 */
if (mresult->exp == mpd_etiny(&mctx)) goto skip;
if (llabs(mop1->exp) > 999999999L) goto skip;
break;
case POWER:
/* decNumber: Issue 9: incorrect power result */
if (mctx.status==(MPD_Inexact|MPD_Overflow|MPD_Rounded) &&
(strcmp("1", dsci) == 0 || strcmp("-1", dsci) == 0))
goto skip;
/* decNumber: Issue 10: rounding to infinity */
if ((mctx.round == MPD_ROUND_FLOOR || mctx.round == MPD_ROUND_DOWN ||
mctx.round == MPD_ROUND_05UP || mctx.round == MPD_ROUND_CEILING) &&
mpd_isfinite(mresult) && decNumberIsInfinite(&dresult))
goto skip;
/* decNumber: Integer exponents must be in the range
-1999999997 through +999999999. */
if (!mpd_isnan(mresult) && decNumberIsNaN(&dresult))
goto skip;
/* decNumber: Flag-issue 2: Clamped status in power */
if (!(mctx.status&MPD_Clamped) && (dctx.status&DEC_Clamped)) {
skip_contexts = 1;
}
/* decNumber: Flag-issue 3: Rounded status in power */
if (!!(mctx.status&MPD_Rounded) != !!(dctx.status&DEC_Rounded)) {
skip_contexts = 1;
}
/* Always having the same values for Subnormal/Underflow is hard */
if (!!(mctx.status&MPD_Underflow) != !!(dctx.status&DEC_Underflow)) {
skip_contexts = 1;
}
/* Always having the same values for Overflow is hard */
if (!!(mctx.status&MPD_Overflow) != !!(dctx.status&DEC_Overflow)) {
skip_contexts = 1;
}
break;
case QUANTIZE:
/* decNumber: Issue 11: Infinity result with normal operand */
if (mpd_isnan(mresult) && decNumberIsInfinite(&dresult))
goto skip;
/* decNumber: Issue 12: Inexact/Rounded with NaN result */
if (mpd_isnan(mresult) && decNumberIsNaN(&dresult)) {
if ((mctx.status&MPD_Invalid_operation) &&
(dctx.status&DEC_Invalid_operation) &&
((dctx.status|DEC_Inexact) || (dctx.status|DEC_Rounded)))
goto skip;
}
break;
case REM: case NREM: case REMNEAR:
/* decNumber: Issue 13: ideal exponent and status for
remainder(0, x), rem_near(0, x) */
if (mpd_iszero(mresult) && decNumberIsZero(&dresult) &&
(mctx.status&MPD_Clamped))
goto skip;
break;
case ROTATE:
/* decNumber: Issue 14: rotate with excess input digits */
if (mop1->digits > mctx.prec) goto skip;
break;
case SCALEB:
/* decNumber: Issue 15: scaleb with excess input digits */
if (mop1->digits > mctx.prec) goto skip;
/* decNumber limits the maximum exponent difference. */
if ((dctx.status&DEC_Invalid_operation) &&
!(mctx.status&MPD_Invalid_operation))
goto skip;
break;
case SHIFT:
/* decNumber: Issue 16: shift with excess input digits */
if (mop1->digits > mctx.prec) goto skip;
break;
case SQRT:
/* decNumber: Issue 17: squareroot zero padding */
have_dot = 0;
mp = msci;
dp = dsci;
while (*dp && *mp == *dp) {
if (*dp == '.') have_dot = 1;
dp++; mp++;
}
if (have_dot && (*dp == '\0' || *dp == 'E') && *mp == '0') {
/* skip zeros */
while (*mp == '0') mp++;
}
if (strcmp(dp, mp) == 0) {
/* decNumber: Issue 20: square root status */
if ((mctx.status&MPD_Rounded) &&
(mctx.status&MPD_Inexact) &&
!(dctx.status&DEC_Rounded) &&
!(dctx.status&DEC_Inexact)) {
goto skip;
}
goto cmp_contexts;
}
break;
case TOINT: case TOINTX:
/* Might be respecified. */
if (mop1->digits > mctx.prec) {
if (mctx.clamp) goto skip;
if (mctx.status==MPD_Invalid_operation &&
(dctx.status&DEC_Invalid_operation))
/* decNumber sets Rounded and/or Inexact */
goto skip;
/* quantize */
if (!!(mctx.status&MPD_Invalid_operation) !=
!!(dctx.status&DEC_Invalid_operation))
goto skip;
/* quantize infinity in decNumber */
if (decNumberIsInfinite(&dresult))
goto skip;
}
break;
default:
break;
}
if (strcmp(msci, dsci) || strcmp(meng, deng)) {
/* resolve_ulp() is undefined for NaNs. */
if (!!mpd_isnan(mresult) != !!decNumberIsNaN(&dresult)) {
print_err(t, msci, dsci, meng, deng);
retval = EXIT_FAILURE;
if (exit_on_error) {
exit(retval);
}
}
else if (!resolve_ulp(t, dsci)) {
print_err(t, msci, dsci, meng, deng);
retval = EXIT_FAILURE;
if (exit_on_error) {
exit(retval);
}
}
/* Contexts are likely different even if ulpdiff is fine. */
goto out;
}
cmp_contexts:
if (!skip_contexts) {
if (!compare_contexts()) {
print_err(t, msci, dsci, meng, deng);
retval = EXIT_FAILURE;
if (exit_on_error) {
exit(retval);
}
}
}
else {
skipped_flags++;
}
out:
mpd_free(msci);
mpd_free(meng);
return;
skip:
skipped++;
goto out;
}
/* ========================================================================== */
/* Function Types */
/* ========================================================================== */
#ifndef PRINT_ONLY
#define unary_mfunc void (*)(mpd_t *, const mpd_t *, mpd_context_t *)
#define unary_dfunc decNumber * (*)(decNumber *, const decNumber *, decContext *)
static void
un(const testset *t)
{
#ifdef DEBUG
printf("%s %s\n", t->name, cop1); fflush(stdout);
#endif
mpd_set_string(mop1, cop1, &m_maxctx);
decNumberFromString(&dop1, cop1, &d_maxctx);
mctx.status = 0;
dctx.status = 0;
((unary_mfunc)t->mfunc)(mresult, mop1, &mctx);
((unary_dfunc)t->dfunc)(&dresult, &dop1, &dctx);
counter++;
nans += !!mpd_isnan(mresult);
infinities += !!mpd_isinfinite(mresult);
subnormals += !!mpd_issubnormal(mresult, &mctx);
zeros += !!mpd_iszero(mresult);
compare_results(t);
}
static void
unlog(const testset *t)
{
un(t);
}
#define unarychar_mfunc char * (*)(const mpd_t *, int)
#define unarychar_dfunc char * (*)(const decNumber *, char *)
static void
unchar(const testset *t)
{
char *mcalc;
char *dcalc = dsci;
#ifdef DEBUG
printf("%s %s\n", t->name, cop1);
fflush(stdout);
#endif
/* status should be set in conversion */
mctx.status = 0;
dctx.status = 0;
mpd_set_string(mop1, cop1, &mctx);
decNumberFromString(&dop1, cop1, &dctx);
mcalc = ((unarychar_mfunc)t->mfunc)(mop1, 1);
((unarychar_dfunc)t->dfunc)(&dop1, dcalc);
if (strcmp(mcalc, dcalc) || !compare_contexts()) {
print_err(t, mcalc, dcalc, NULL, NULL);
}
mpd_free(mcalc);
}
#define unarycchar_mfunc const char * (*)(const mpd_t *, mpd_context_t *)
#define unarycchar_dfunc const char * (*)(const decNumber *, decContext *)
static void
uncchar(const testset *t)
{
const char *mcalc;
const char *dcalc;
#ifdef DEBUG
printf("%s %s\n", t->name, cop1);
fflush(stdout);
#endif
mpd_set_string(mop1, cop1, &m_maxctx);
decNumberFromString(&dop1, cop1, &d_maxctx);
mctx.status = 0;
dctx.status = 0;
mcalc = ((unarycchar_mfunc)t->mfunc)(mop1, &mctx);
dcalc = ((unarycchar_dfunc)t->dfunc)(&dop1, &dctx);
if (strcmp(mcalc, dcalc) || !compare_contexts()) {
print_err(t, mcalc, dcalc, NULL, NULL);
}
}
#define unarycopy_mfunc void (*)(mpd_t *, const mpd_t *, mpd_context_t *)
#define unarycopy_dfunc decNumber * (*)(decNumber *, const decNumber *)
static void
uncpy(const testset *t)
{
#ifdef DEBUG
printf("%s %s\n", t->name, cop1);
fflush(stdout);
#endif
mpd_set_string(mop1, cop1, &m_maxctx);
decNumberFromString(&dop1, cop1, &d_maxctx);
mctx.status = 0;
dctx.status = 0;
((unarycopy_mfunc)t->mfunc)(mresult, mop1, &mctx);
((unarycopy_dfunc)t->dfunc)(&dresult, &dop1);
counter++;
nans += !!mpd_isnan(mresult);
infinities += !!mpd_isinfinite(mresult);
subnormals += !!mpd_issubnormal(mresult, &mctx);
zeros += !!mpd_iszero(mresult);
compare_results(t);
}
#define binary_mfunc void (*)(mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *)
#define binary_dfunc decNumber * (*)(decNumber *, const decNumber *, const decNumber *, decContext *)
static void
bn(const testset *t)
{
#ifdef DEBUG
printf("%s %s %s\n", t->name, cop1, cop2);
fflush(stdout);
#endif
mpd_set_string(mop1, cop1, &m_maxctx);
decNumberFromString(&dop1, cop1, &d_maxctx);
mpd_set_string(mop2, cop2, &m_maxctx);
decNumberFromString(&dop2, cop2, &d_maxctx);
mctx.status = 0;
dctx.status = 0;
((binary_mfunc)t->mfunc)(mresult, mop1, mop2, &mctx);
((binary_dfunc)t->dfunc)(&dresult, &dop1, &dop2, &dctx);
counter++;
nans += !!mpd_isnan(mresult);
infinities += !!mpd_isinfinite(mresult);
subnormals += !!mpd_issubnormal(mresult, &mctx);
zeros += !!mpd_iszero(mresult);
compare_results(t);
}
static void
bnlog(const testset *t)
{
bn(t);
}
#define binarycpy_mfunc void (*)(mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *)
#define binarycpy_dfunc decNumber *(*)(decNumber *, const decNumber *, const decNumber *)
static void
bncpy(const testset *t)
{
#ifdef DEBUG
printf("%s %s %s\n", t->name, cop1, cop2);
fflush(stdout);
#endif
mpd_set_string(mop1, cop1, &m_maxctx);
decNumberFromString(&dop1, cop1, &d_maxctx);
mpd_set_string(mop2, cop2, &m_maxctx);
decNumberFromString(&dop2, cop2, &d_maxctx);
mctx.status = 0;
dctx.status = 0;
((binarycpy_mfunc)t->mfunc)(mresult, mop1, mop2, &mctx);
((binarycpy_dfunc)t->dfunc)(&dresult, &dop1, &dop2);
counter++;
nans += !!mpd_isnan(mresult);
infinities += !!mpd_isinfinite(mresult);
subnormals += !!mpd_issubnormal(mresult, &mctx);
zeros += !!mpd_iszero(mresult);
compare_results(t);
}
#define binarycmpctx_mfunc int (*)(mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *)
#define binarycmpctx_dfunc decNumber *(*)(decNumber *, const decNumber *, const decNumber *, decContext *)
static void
bncmpctx(const testset *t)
{
#ifdef DEBUG
printf("%s %s %s\n", t->name, cop1, cop2);
fflush(stdout);
#endif
mpd_set_string(mop1, cop1, &m_maxctx);
decNumberFromString(&dop1, cop1, &d_maxctx);
mpd_set_string(mop2, cop2, &m_maxctx);
decNumberFromString(&dop2, cop2, &d_maxctx);
mctx.status = 0;
dctx.status = 0;
(void)((binarycmpctx_mfunc)t->mfunc)(mresult, mop1, mop2, &mctx);
((binarycmpctx_dfunc)t->dfunc)(&dresult, &dop1, &dop2, &dctx);
counter++;
nans += !!mpd_isnan(mresult);
infinities += !!mpd_isinfinite(mresult);
subnormals += !!mpd_issubnormal(mresult, &mctx);
zeros += !!mpd_iszero(mresult);
compare_results(t);
}
#define binarycmp_mfunc int (*)(mpd_t *, const mpd_t *, const mpd_t *)
#define binarycmp_dfunc decNumber *(*)(decNumber *, const decNumber *, const decNumber *, decContext *)
static void
bncmp(const testset *t)
{
#ifdef DEBUG
printf("%s %s %s\n", t->name, cop1, cop2);
fflush(stdout);
#endif
mpd_set_string(mop1, cop1, &m_maxctx);
decNumberFromString(&dop1, cop1, &d_maxctx);
mpd_set_string(mop2, cop2, &m_maxctx);
decNumberFromString(&dop2, cop2, &d_maxctx);
mctx.status = 0;
dctx.status = 0;
(void)((binarycmp_mfunc)t->mfunc)(mresult, mop1, mop2);
((binarycmp_dfunc)t->dfunc)(&dresult, &dop1, &dop2, &dctx);
compare_results(t);
}
#define binarysq_mfunc int (*)(const mpd_t *, const mpd_t *)
#define binarysq_dfunc int (*)(const decNumber *, const decNumber *)
static void
bnsameq(const testset *t)
{
int mres, dres;
#ifdef DEBUG
printf("%s %s %s\n", t->name, cop1, cop2);
fflush(stdout);
#endif
mpd_set_string(mop1, cop1, &m_maxctx);
decNumberFromString(&dop1, cop1, &d_maxctx);
mpd_set_string(mop2, cop2, &m_maxctx);
decNumberFromString(&dop2, cop2, &d_maxctx);
mctx.status = 0;
dctx.status = 0;
mres = ((binarysq_mfunc)t->mfunc)(mop1, mop2);
dres = ((binarysq_dfunc)t->dfunc)(&dop1, &dop2);
if (mres != dres || !compare_contexts()) {
printf("%s\n", "XXX");
fflush(stdout);
}
}
#define ternary_mfunc void (*)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_t *, mpd_context_t *)
#define ternary_dfunc decNumber *(*)(decNumber *, const decNumber *, const decNumber *, const decNumber *, decContext *)
static void
tn(const testset *t)
{
#ifdef DEBUG
printf("%s %s %s %s\n", t->name, cop1, cop2, cop3);
fflush(stdout);
#endif
mpd_set_string(mop1, cop1, &m_maxctx);
decNumberFromString(&dop1, cop1, &d_maxctx);
mpd_set_string(mop2, cop2, &m_maxctx);
decNumberFromString(&dop2, cop2, &d_maxctx);
mpd_set_string(mop3, cop3, &m_maxctx);
decNumberFromString(&dop3, cop3, &d_maxctx);
mctx.status = 0;
dctx.status = 0;
((ternary_mfunc)t->mfunc)(mresult, mop1, mop2, mop3, &mctx);
((ternary_dfunc)t->dfunc)(&dresult, &dop1, &dop2, &dop3, &dctx);
counter++;
nans += !!mpd_isnan(mresult);
infinities += !!mpd_isinfinite(mresult);
subnormals += !!mpd_issubnormal(mresult, &mctx);
zeros += !!mpd_iszero(mresult);
compare_results(t);
}
#else
/* PRINT_ONLY */
static void
un(const testset *t)
{
printf("%s %s\n", t->name, cop1);
fflush(stdout);
mpd_set_string(mop1, cop1, &m_maxctx);
if (m_maxctx.status&MPD_Conversion_syntax) {
abort();
}
counter++;
}
static void unlog(const testset *t) { un(t); }
static void unchar(const testset *t) { un(t); }
static void uncchar(const testset *t) { un(t); }
static void uncpy(const testset *t) { un(t); }
static void
bn(const testset *t)
{
printf("%s %s %s\n", t->name, cop1, cop2);
fflush(stdout);
mpd_set_string(mop1, cop1, &m_maxctx);
mpd_set_string(mop2, cop2, &m_maxctx);
if (m_maxctx.status&MPD_Conversion_syntax) {
abort();
}
counter++;
}
static void bnlog(const testset *t) { bn(t); }
static void bncmp(const testset *t) { bn(t); }
static void bncpy(const testset *t) { bn(t); }
static void bncmpctx(const testset *t) { bn(t); }
static void bnsameq(const testset *t) { bn(t); }
static void
tn(const testset *t)
{
printf("%s %s %s %s\n", t->name, cop1, cop2, cop3);
fflush(stdout);
mpd_set_string(mop1, cop1, &m_maxctx);
mpd_set_string(mop2, cop2, &m_maxctx);
mpd_set_string(mop3, cop3, &m_maxctx);
if (m_maxctx.status&MPD_Conversion_syntax) {
abort();
}
counter++;
}
#endif /* PRINT_ONLY */
/* ========================================================================== */
/* Adapt problematic functions */
/* ========================================================================== */
static const char *
dn_class(decNumber *x, decContext *ctx)
{
return decNumberClassToString(decNumberClass(x, ctx));
}
static int
dn_samequantum(decNumber *x, decNumber *y)
{
decNumberSameQuantum(&dresult, x, y);
return decNumberToInt32(&dresult, &dctx);
}
/* ========================================================================== */
/* Unary functions */
/* ========================================================================== */
#define cvp const void *
static const testset testsets[] = {
/* unary functions */
{ABS, "abs", 0, UN, un, (cvp)mpd_abs, (cvp)decNumberAbs},
{EXP, "exp", RE, UN, un, (cvp)mpd_exp, (cvp)decNumberExp},
{LN, "ln", RE, UN, un, (cvp)mpd_ln, (cvp)decNumberLn},
{LOG10, "log10", RE, UN, un, (cvp)mpd_log10, (cvp)decNumberLog10},
{LOGB, "logb", 0, UN, un, (cvp)mpd_logb, (cvp)decNumberLogB},
{MINUS, "minus", 0, UN, un, (cvp)mpd_minus, (cvp)decNumberMinus},
{NEXTMINUS, "nextminus", 0, UN, un, (cvp)mpd_next_minus, (cvp)decNumberNextMinus},
{NEXTPLUS, "nextplus", 0, UN, un, (cvp)mpd_next_plus, (cvp)decNumberNextPlus},
{PLUS, "plus", 0, UN, un, (cvp)mpd_plus, (cvp)decNumberPlus},
{REDUCE, "reduce", 0, UN, un, (cvp)mpd_reduce, (cvp)decNumberReduce},
{SQRT, "squareroot", 0, UN, un, (cvp)mpd_sqrt, (cvp)decNumberSquareRoot},
{TOINT, "tointegral", 0, UN, un, (cvp)mpd_round_to_int, (cvp)decNumberToIntegralValue},
{TOINTX, "tointegralx", 0, UN, un, (cvp)mpd_round_to_intx, (cvp)decNumberToIntegralExact},
/* char unary functions */
{TOSCI, "tosci", 0, UN, unchar, (cvp)mpd_to_sci, (cvp)decNumberToString},
{TOENG, "toeng", 0, UN, unchar, (cvp)mpd_to_eng, (cvp)decNumberToEngString},
/* const char unary functions */
{CLASS, "class", 0, UN, uncchar, (cvp)mpd_class, (cvp)dn_class},
/* copy */
{COPY, "copy", 0, UN, uncpy, (cvp)mpd_copy, (cvp)decNumberCopy},
{COPYABS, "copyabs", 0, UN, uncpy, (cvp)mpd_copy_abs, (cvp)decNumberCopyAbs},
{COPYNEG, "copynegate", 0, UN, uncpy, (cvp)mpd_copy_negate, (cvp)decNumberCopyNegate},
/* logical unary functions */
{INVERT, "invert", 0, UN, unlog, (cvp)mpd_invert, (cvp)decNumberInvert},
/* binary functions */
{ADD, "add", 0, BN, bn, (cvp)mpd_add, (cvp)decNumberAdd},
{DIV, "divide", 0, BN, bn, (cvp)mpd_div, (cvp)decNumberDivide},
{NDIV, "ndivide", 0, BN, bn, (cvp)mpd_test_newtondiv, (cvp)decNumberDivide},
{DIVINT, "divideint", 0, BN, bn, (cvp)mpd_divint, (cvp)decNumberDivideInteger},
{MAX, "max", 0, BN, bn, (cvp)mpd_max, (cvp)decNumberMax},
{MAXMAG, "maxmag", 0, BN, bn, (cvp)mpd_max_mag, (cvp)decNumberMaxMag},
{MIN, "min", 0, BN, bn, (cvp)mpd_min, (cvp)decNumberMin},
{MINMAG, "minmag", 0, BN, bn, (cvp)mpd_min_mag, (cvp)decNumberMinMag},
{MULTIPLY, "multiply", 0, BN, bn, (cvp)mpd_mul, (cvp)decNumberMultiply},
{NEXTTWRD, "nexttoward", 0, BN, bn, (cvp)mpd_next_toward, (cvp)decNumberNextToward},
{POWER, "power", RE, BN, bn, (cvp)mpd_pow, (cvp)decNumberPower},
{QUANTIZE, "quantize", 0, BN, bn, (cvp)mpd_quantize, (cvp)decNumberQuantize},
{REM, "remainder", 0, BN, bn, (cvp)mpd_rem, (cvp)decNumberRemainder},
{NREM, "nremainder", 0, BN, bn, (cvp)mpd_test_newtonrem, (cvp)decNumberRemainder},
{REMNEAR, "remaindernear", 0, BN, bn, (cvp)mpd_rem_near, (cvp)decNumberRemainderNear},
{ROTATE, "rotate", 0, BN, bn, (cvp)mpd_rotate, (cvp)decNumberRotate},
{SCALEB, "scaleb", 0, BN, bn, (cvp)mpd_scaleb, (cvp)decNumberScaleB},
{SHIFT, "shift", 0, BN, bn, (cvp)mpd_shift, (cvp)decNumberShift},
{SUBTRACT, "subtract", 0, BN, bn, (cvp)mpd_sub, (cvp)decNumberSubtract},
/* logical binary functions */
{AND, "and", 0, BN, bnlog, (cvp)mpd_and, (cvp)decNumberAnd},
{OR, "or", 0, BN, bnlog, (cvp)mpd_or, (cvp)decNumberOr},
{XOR, "xor", 0, BN, bnlog, (cvp)mpd_xor, (cvp)decNumberXor},
/* copy binary functions */
{COPYSIGN, "copysign", 0, BN, bncpy, (cvp)mpd_copy_sign, (cvp)decNumberCopySign},
/* compare binary functions taking a context */
{CMP, "compare", 0, BN, bncmpctx, (cvp)mpd_compare, (cvp)decNumberCompare},
{CMPSIG, "comparesig", 0, BN, bncmpctx, (cvp)mpd_compare_signal, (cvp)decNumberCompareSignal},
/* compare binary functions */
{CMPT, "comparetotal", 0, BN, bncmp, (cvp)mpd_compare_total, (cvp)decNumberCompareTotal},
{CMPTMAG, "comparetotalmag", 0, BN, bncmp, (cvp)mpd_compare_total_mag, (cvp)decNumberCompareTotalMag},
/* int binary functions */
{SAMEQUANT, "samequantum", 0, BN, bnsameq, (cvp)mpd_same_quantum, (cvp)dn_samequantum},
/* ternary functions */
{FMA, "fma", RE, TN, tn, (cvp)mpd_fma, (cvp)decNumberFMA},
{MAXID, NULL, -1, -1, NULL, NULL, NULL}
};
/* ========================================================================== */
/* Test functions */
/* ========================================================================== */
static inline char *
indicator(char *s)
{
*s++ = random()%2 ? 'e' : 'E';
*s = '\0';
return s;
}
static inline char *
digits(char *s, mpd_ssize_t len)
{
mpd_ssize_t n;
for (n = 0; n < len; n++) {
*s++ = random()%10 + '0';
}
*s = '\0';
return s;
}
static inline char *
decimal_part(char *s, mpd_ssize_t len)
{
long x = random()%100;
mpd_ssize_t intlen, fraclen;
if (x > 80) {
s = digits(s, len);
}
else if (x > 20) {
intlen = random()%(len+1);
fraclen = len - intlen;
s = digits(s, intlen);
*s++ = '.';
s = digits(s, fraclen);
}
else {
intlen = random()%(len+1)+1;
*s++ = '.';
s = digits(s, intlen);
}
return s;
}
static inline char *
expdigits(char *s)
{
mpd_ssize_t x, q, d;
int j;
x = (random()%2) ? mctx.emax : 200;
if (random()%2) {
*s++ = '-';
x = random() % (2*x+1);
}
else {
if (random()%2)
*s++ = '+';
x = random() % (x+1);
}
j = mpd_exp_digits(x) - 1;
for (; j != 0; --j) {
d = rt_pow10[j];
q = x / d;
x -= d * q;
*s++ = '0' + (char)q;
}
*s++ = '0' + (char)x;
*s = '\0';
return s;
}
static inline char *
exponentpart(char *s)
{
s = indicator(s);
s = expdigits(s);
return s;
}
static inline char *
infinity(char *s)
{
*s++ = 'i';
*s++ = 'n';
*s++ = 'f';
if (random()%2) {
*s++ = 'i';
*s++ = 'n';
*s++ = 'i';
*s++ = 't';
*s++ = 'y';
}
*s = '\0';
return s;
}
static inline char *
dcnan(char *s, mpd_ssize_t len)
{
if (random()%2) {
*s++ = 's';
}
*s++ = 'N';
*s++ = 'a';
*s++ = 'N';
*s = '\0';
if (random()%2) {
s = digits(s, len);
}
return s;
}
static inline char *
numeric_value(char *s, mpd_ssize_t maxprec)
{
long x = random()%100;
if (x > 95) {
return infinity(s);
}
s = decimal_part(s, maxprec);
if (x > 20) {
s = exponentpart(s);
}
return s;
}
static inline char *
numeric_string(char *s, mpd_ssize_t maxprec)
{
long x = random()%100;
if (x > 98) {
return dcnan(s, maxprec);
}
return numeric_value(s, maxprec);
}
static inline char *
randdec(char *s, mpd_ssize_t maxprec)
{
return numeric_string(s, maxprec);
}
static inline char *
leading_zero_pattern(char *s, mpd_ssize_t maxzeros)
{
long nzeros = random()%(maxzeros+1);
long ndigits = random()%10;
long n;
*s++ = '.';
memset(s, '0', nzeros);
s += nzeros;
for (n = 0; n < ndigits; n++) {
*s++ = random()%10 + '0';
}
*s = '\0';
return s;
}
static inline char *
double_zero_pattern(char *s, mpd_ssize_t maxzeros)
{
long nzeros = random()%(maxzeros+1);
long ndigits = random()%10;
long n;
*s++ = '.';
memset(s, '0', nzeros);
s += nzeros;
for (n = 0; n < ndigits; n++) {
*s++ = random()%10 + '0';
}
nzeros = random()%(maxzeros+1);
ndigits = random()%10;
memset(s, '0', nzeros);
s += nzeros;
for (n = 0; n < ndigits; n++) {
*s++ = random()%10 + '0';
}
*s = '\0';
return s;
}
static inline char *
leading_nine_pattern(char *s, mpd_ssize_t maxnines)
{
long nines = random()%(maxnines+1);
long ndigits = random()%10;
long n = 0;
*s++ = '.';
memset(s, '9', nines);
s += nines;
for (n = 0; n < ndigits; n++) {
*s++ = random()%10 + '0';
}
*s = '\0';
return s;
}
static inline char *
small_integer(char *s)
{
long ndigits = random()%3+1;
long n = 0;
for (n = 0; n < ndigits; n++) {
*s++ = random()%10 + '0';
}
*s = '\0';
return s;
}
static inline char *
small_neg_integer(char *s)
{
long ndigits = random()%3+1;
long n = 0;
*s++ = '-';
for (n = 0; n < ndigits; n++) {
*s++ = random()%10 + '0';
}
*s = '\0';
return s;
}
/* close to minus infinity */
static void
close_to_minus_inf_less(char *s, mpd_ssize_t prec)
{
mpd_ssize_t zeros = random()%(prec+30);
*s++ = '-';
*s++ = '1';
*s++ = '.';
*s++ = '0';
memset(s, '0', zeros);
s += zeros;
s = digits(s, prec);
snprintf(s, MAXOP-prec-30, "e%"PRI_mpd_ssize_t"", mctx.emax+1);
}
static void
close_to_minus_inf_greater(char *s, mpd_ssize_t prec)
{
mpd_ssize_t nines = random()%(prec+30);
*s++ = '-';
*s++ = '9';
*s++ = '.';
*s++ = '9';
memset(s, '9', nines);
s += nines;
s = digits(s, prec);
snprintf(s, MAXOP-prec-30, "e%"PRI_mpd_ssize_t"", mctx.emax);
}
/* close to minus one */
static void
close_to_minus_one_less(char *s, mpd_ssize_t prec)
{
mpd_ssize_t zeros = random()%(prec+30);
*s++ = '-';
*s++ = '1';
*s++ = '.';
memset(s, '0', zeros);
s += zeros;
digits(s, prec);
}
static void
close_to_minus_one_greater(char *s, mpd_ssize_t prec)
{
mpd_ssize_t nines = random()%(prec+30);
*s++ = '-';
*s++ = '0';
*s++ = '.';
*s++ = '9';
memset(s, '9', nines);
s += nines;
digits(s, prec);
}
/* close to minus emin */
static void
close_to_minus_emin_less(char *s, mpd_ssize_t prec)
{
mpd_ssize_t zeros = random()%(prec+30);
*s++ = '-';
*s++ = '1';
*s++ = '.';
*s++ = '0';
memset(s, '0', zeros);
s += zeros;
s = digits(s, prec);
snprintf(s, MAXOP-prec-30, "e%"PRI_mpd_ssize_t"", mctx.emin);
}
static void
close_to_minus_emin_greater(char *s, mpd_ssize_t prec)
{
mpd_ssize_t nines = random()%(prec+30);
*s++ = '-';
*s++ = '0';
*s++ = '.';
*s++ = '9';
memset(s, '9', nines);
s += nines;
s = digits(s, prec);
snprintf(s, MAXOP-prec-30, "e%"PRI_mpd_ssize_t"", mctx.emin);
}
/* close to minus etiny */
static void
close_to_minus_etiny_less(char *s, mpd_ssize_t prec)
{
mpd_ssize_t zeros = random()%(prec+30);
*s++ = '-';
*s++ = '1';
*s++ = '.';
*s++ = '0';
memset(s, '0', zeros);
s += zeros;
s = digits(s, prec);
snprintf(s, MAXOP-prec-30, "e%"PRI_mpd_ssize_t"", mpd_etiny(&mctx));
}
static void
close_to_minus_etiny_greater(char *s, mpd_ssize_t prec)
{
mpd_ssize_t nines = random()%(prec+30);
*s++ = '-';
*s++ = '0';
*s++ = '.';
*s++ = '9';
memset(s, '9', nines);
s += nines;
s = digits(s, prec);
snprintf(s, MAXOP-prec-30, "e%"PRI_mpd_ssize_t"", mpd_etiny(&mctx));
}
/* close to zero */
static void
close_to_zero_less(char *s, mpd_ssize_t prec)
{
mpd_ssize_t zeros = random()%(prec+30);
*s++ = '-';
*s++ = '0';
*s++ = '.';
*s++ = '0';
memset(s, '0', zeros);
s += zeros;
digits(s, prec);
}
static void
close_to_zero_greater(char *s, mpd_ssize_t prec)
{
mpd_ssize_t zeros = random()%(prec+30);
*s++ = '0';
*s++ = '.';
*s++ = '0';
memset(s, '0', zeros);
s += zeros;
digits(s, prec);
}
/* close to etiny */
static void
close_to_etiny_less(char *s, mpd_ssize_t prec)
{
mpd_ssize_t nines = random()%(prec+30);
*s++ = '0';
*s++ = '.';
*s++ = '9';
memset(s, '9', nines);
s += nines;
s = digits(s, prec);
snprintf(s, MAXOP-prec-30, "e%"PRI_mpd_ssize_t"", mpd_etiny(&mctx));
}
static void
close_to_etiny_greater(char *s, mpd_ssize_t prec)
{
mpd_ssize_t zeros = random()%(prec+30);
*s++ = '1';
*s++ = '.';
*s++ = '0';
memset(s, '0', zeros);
s += zeros;
s = digits(s, prec);
snprintf(s, MAXOP-prec-30, "e%"PRI_mpd_ssize_t"", mpd_etiny(&mctx));
}
/* close to emin */
static void
close_to_emin_less(char *s, mpd_ssize_t prec)
{
mpd_ssize_t nines = random()%(prec+30);
*s++ = '0';
*s++ = '.';
*s++ = '9';
memset(s, '9', nines);
s += nines;
s = digits(s, prec);
snprintf(s, MAXOP-prec-30, "e%"PRI_mpd_ssize_t"", mctx.emin);
}
static void
close_to_emin_greater(char *s, mpd_ssize_t prec)
{
mpd_ssize_t zeros = random()%(prec+30);
*s++ = '1';
*s++ = '.';
*s++ = '0';
memset(s, '0', zeros);
s += zeros;
s = digits(s, prec);
snprintf(s, MAXOP-prec-30, "e%"PRI_mpd_ssize_t"", mctx.emin);
}
/* close to one */
static void
close_to_one_less(char *s, mpd_ssize_t prec)
{
mpd_ssize_t nines = random()%(prec+30);
*s++ = '0';
*s++ = '.';
*s++ = '9';
memset(s, '9', nines);
s += nines;
digits(s, prec);
}
static void
close_to_one_greater(char *s, mpd_ssize_t prec)
{
mpd_ssize_t zeros = random()%(prec+30);
*s++ = '1';
*s++ = '.';
memset(s, '0', zeros);
s += zeros;
digits(s, prec);
}
/* close to infinity */
static void
close_to_inf_less(char *s, mpd_ssize_t prec)
{
mpd_ssize_t nines = random()%(prec+30);
*s++ = '9';
*s++ = '.';
*s++ = '9';
memset(s, '9', nines);
s += nines;
s = digits(s, prec);
snprintf(s, MAXOP-prec-30, "e%"PRI_mpd_ssize_t"", mctx.emax);
}
static void
close_to_inf_greater(char *s, mpd_ssize_t prec)
{
mpd_ssize_t zeros = random()%(prec+30);
*s++ = '1';
*s++ = '.';
*s++ = '0';
memset(s, '0', zeros);
s += zeros;
s = digits(s, prec);
snprintf(s, MAXOP-prec-30, "e%"PRI_mpd_ssize_t"", mctx.emax+1);
}
static void (*close_funcs[])(char *, mpd_ssize_t) = {
close_to_minus_inf_less, close_to_minus_inf_greater, close_to_minus_one_less,
close_to_minus_one_greater, close_to_minus_emin_less, close_to_minus_emin_greater,
close_to_minus_etiny_less, close_to_minus_etiny_greater, close_to_zero_less,
close_to_zero_greater, close_to_etiny_less, close_to_etiny_greater,
close_to_emin_less, close_to_emin_greater, close_to_one_less,
close_to_one_greater, close_to_inf_less, close_to_inf_greater
};
static void
un_close_to_pow10(const testset *t, mpd_ssize_t prec)
{
mpd_ssize_t n;
/* integer nines */
n = prec+30;
memset(cop1, '9', n);
for (n = prec+30; n >= 1; n--) {
cop1[n] = '\0';
t->testfunc(t);
leading_zero_pattern(cop1+n, 50);
t->testfunc(t);
double_zero_pattern(cop1+n, 50);
t->testfunc(t);
leading_nine_pattern(cop1+n, 50);
t->testfunc(t);
}
/* negative integer nines */
n = prec+30;
cop1[0] = '-';
memset(cop1+1, '9', n);
for (n = 1+prec+30; n >= 2; n--) {
cop1[n] = '\0';
t->testfunc(t);
leading_zero_pattern(cop1+n, 50);
t->testfunc(t);
double_zero_pattern(cop1+n, 50);
t->testfunc(t);
leading_nine_pattern(cop1+n, 50);
t->testfunc(t);
}
/* integer powers of 10 */
cop1[0] = '1';
n = prec+30;
memset(cop1+1, '0', n);
for (n = 1+prec+30; n >= 1; n--) {
cop1[n] = '\0';
t->testfunc(t);
leading_zero_pattern(cop1+n, 50);
t->testfunc(t);
double_zero_pattern(cop1+n, 50);
t->testfunc(t);
leading_nine_pattern(cop1+n, 50);
t->testfunc(t);
}
/* negative integer powers of 10 */
cop1[0] = '-';
cop1[1] = '1';
n = prec+30;
memset(cop1+2, '0', n);
for (n = 2+prec+30; n >= 2; n--) {
cop1[n] = '\0';
t->testfunc(t);
leading_zero_pattern(cop1+n, 50);
t->testfunc(t);
double_zero_pattern(cop1+n, 50);
t->testfunc(t);
leading_nine_pattern(cop1+n, 50);
t->testfunc(t);
}
}
static void
bn_close_to_pow10(const testset *t, mpd_ssize_t prec)
{
mpd_ssize_t n;
/* 9999999..., small int */
n = prec+30;
memset(cop1, '9', n);
for (n = prec+30; n >= 1; n--) {
cop1[n] = '\0';
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
leading_zero_pattern(cop1+n, 50);
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
double_zero_pattern(cop1+n, 50);
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
leading_nine_pattern(cop1+n, 50);
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
}
/* -999999...., small int */
n = prec+30;
cop1[0] = '-';
memset(cop1+1, '9', n);
for (n = 1+prec+30; n >= 2; n--) {
cop1[n] = '\0';
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
leading_zero_pattern(cop1+n, 50);
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
double_zero_pattern(cop1+n, 50);
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
leading_nine_pattern(cop1+n, 50);
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
}
/* 1000000..., small int */
cop1[0] = '1';
n = prec+30;
memset(cop1+1, '0', n);
for (n = 1+prec+30; n >= 1; n--) {
cop1[n] = '\0';
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
leading_zero_pattern(cop1+n, 50);
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
double_zero_pattern(cop1+n, 50);
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
leading_nine_pattern(cop1+n, 50);
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
}
/* -1000000, small int */
cop1[0] = '-';
cop1[1] = '1';
n = prec+30;
memset(cop1+2, '0', n);
for (n = 2+prec+30; n >= 2; n--) {
cop1[n] = '\0';
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
leading_zero_pattern(cop1+n, 50);
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
double_zero_pattern(cop1+n, 50);
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
leading_nine_pattern(cop1+n, 50);
small_integer(cop2);
t->testfunc(t);
small_neg_integer(cop2);
t->testfunc(t);
}
}
static void
un_incr_digits(const testset *t, mpd_ssize_t prec)
{
char *s;
mpd_ssize_t n;
for (n = 1; n < prec+30; n++) {
digits(cop1, n);
t->testfunc(t);
}
cop1[0] = '-';
for (n = 1; n < prec+30; n++) {
digits(cop1+1, n);
t->testfunc(t);
}
s = cop1;
for (n = 1; n < prec+30; n++) {
s = digits(cop1, n);
exponentpart(s);
t->testfunc(t);
}
cop1[0] = '-';
for (n = 1; n < prec+30; n++) {
s = digits(cop1+1, n);
exponentpart(s);
t->testfunc(t);
}
}
static void
bn_incr_digits(const testset *t, mpd_ssize_t prec)
{
char *s, *r;
mpd_ssize_t n, m;
for (n = 1; n < prec+30; n++) {
digits(cop1, n);
for (m = 1; m < prec+30; m++) {
digits(cop2, n);
t->testfunc(t);
}
}
cop1[0] = '-';
for (n = 1; n < prec+30; n++) {
digits(cop1+1, n);
for (m = 1; m < prec+30; m++) {
digits(cop2, n);
t->testfunc(t);
}
}
s = cop1;
for (n = 1; n < prec+30; n++) {
s = digits(cop1, n);
exponentpart(s);
for (m = 1; m < prec+30; m++) {
r = digits(cop2, n);
exponentpart(r);
t->testfunc(t);
}
}
cop1[0] = '-';
for (n = 1; n < prec+30; n++) {
s = digits(cop1+1, n);
exponentpart(s);
for (m = 1; m < prec+30; m++) {
r = digits(cop2, n);
exponentpart(r);
t->testfunc(t);
}
}
}
static void
un_close_funcs(const testset *t, mpd_ssize_t prec)
{
mpd_ssize_t i, n_func;
n_func = sizeof(close_funcs)/sizeof(*close_funcs);
for (i = 0; i < n_func; i++) {
if (t->restricted) {
if (close_funcs[i] == close_to_minus_inf_less ||
close_funcs[i] == close_to_inf_greater) {
continue;
}
}
close_funcs[i](cop1, prec);
t->testfunc(t);
}
}
static void
bn_close_funcs(const testset *t, mpd_ssize_t prec)
{
mpd_ssize_t i, j, n_func;
n_func = sizeof(close_funcs)/sizeof(*close_funcs);
for (i = 0; i < n_func; i++) {
if (t->restricted) {
if (close_funcs[i] == close_to_minus_inf_less ||
close_funcs[i] == close_to_inf_greater) {
continue;
}
}
for (j = 0; j < n_func; j++) {
if (t->restricted) {
if (close_funcs[i] == close_to_minus_inf_less ||
close_funcs[i] == close_to_inf_greater) {
continue;
}
}
close_funcs[i](cop1, prec);
close_funcs[j](cop2, prec);
t->testfunc(t);
}
}
}
static void
un_randdec(const testset *t, mpd_ssize_t prec)
{
long x;
int i;
for (i = 0; i < 1000; i++) {
x = random()%(2*prec)+1;
randdec(cop1, x);
t->testfunc(t);
}
}
static void
bn_randdec(const testset *t, mpd_ssize_t prec)
{
long x, y;
int i;
for (i = 0; i < 10000; i++) {
x = random()%(2*prec)+1;
y = random()%(2*prec)+1;
randdec(cop1, x);
randdec(cop2, y);
t->testfunc(t);
}
}
static void
tn_randdec(const testset *t, mpd_ssize_t prec)
{
long x, y, z;
int i;
for (i = 0; i < 10000; i++) {
x = random()%(2*prec)+1;
y = random()%(2*prec)+1;
z = random()%(2*prec)+1;
randdec(cop1, x);
randdec(cop2, y);
randdec(cop3, z);
t->testfunc(t);
}
}
static void
test_unary(const testset *t, mpd_ssize_t prec)
{
un_close_to_pow10(t, prec);
un_close_funcs(t, prec);
un_incr_digits(t, prec);
un_randdec(t, prec);
}
static void
test_binary(const testset *t, mpd_ssize_t prec)
{
bn_close_to_pow10(t, prec);
bn_close_funcs(t, prec);
bn_incr_digits(t, prec);
bn_randdec(t, prec);
}
static void
test_ternary(const testset *t, mpd_ssize_t prec)
{
tn_randdec(t, prec);
}
static void
test_all(const testset *t)
{
int i, j, n_round;
printf("testing %s ...\n", t->name);
fflush(stdout);
n_round = sizeof(mround) / sizeof(*mround);
/* Small precisions */
for (mctx.prec = 1; mctx.prec <= 5; mctx.prec++) {
dctx.digits = (int32_t)mctx.prec;
for (mctx.emax = 1; mctx.emax <= 5; mctx.emax++) {
if (mctx.prec > mctx.emax) continue;
dctx.emax = (int32_t)mctx.emax;
mctx.emin = -mctx.emax;
dctx.emin = (int32_t)mctx.emin;
if (verbose) {
printf(" prec: %"PRI_mpd_ssize_t" emin: %"PRI_mpd_ssize_t""
" emax: %"PRI_mpd_ssize_t"\n",
mctx.prec, mctx.emin, mctx.emax);
fflush(stdout);
}
for (i = 0; i < n_round; i++) {
mctx.round = mround[i];
dctx.round = dround[i];
for (mctx.clamp = 0; mctx.clamp <= 1; mctx.clamp++) {
dctx.clamp = mctx.clamp;
switch (t->type) {
case UN:
test_unary(t, mctx.prec);
break;
case BN:
test_binary(t, mctx.prec);
break;
case TN:
test_ternary(t, mctx.prec);
break;
default:
abort();
break;
}
}
}
}
}
/* IEEE contexts */
for (i = 0; i < IEEE_MAX; i++) {
mctx.prec = ieee_prec[i];
mctx.emax = ieee_emax[i];
mctx.emin = ieee_emin[i];
dctx.digits = (int32_t)mctx.prec;
dctx.emax = (int32_t)mctx.emax;
dctx.emin = (int32_t)mctx.emin;
if (verbose) {
printf(" prec: %"PRI_mpd_ssize_t" emin: %"PRI_mpd_ssize_t""
" emax: %"PRI_mpd_ssize_t"\n",
mctx.prec, mctx.emin, mctx.emax);
fflush(stdout);
}
for (j = 0; j < n_round; j++) {
mctx.round = mround[j];
dctx.round = dround[j];
for (mctx.clamp = 0; mctx.clamp <= 1; mctx.clamp++) {
dctx.clamp = mctx.clamp;
switch (t->type) {
case UN:
test_unary(t, mctx.prec);
break;
case BN:
test_binary(t, mctx.prec);
break;
case TN:
test_ternary(t, mctx.prec);
break;
default:
abort();
break;
}
}
}
}
#ifdef CONFIG_64
mctx.emax = 999999999;
mctx.emin = -999999999;
dctx.emax = (int32_t)mctx.emax;
dctx.emin = (int32_t)mctx.emin;
#else
mctx.emax = 425000000;
mctx.emin = -425000000;
dctx.emax = (int32_t)mctx.emax;
dctx.emin = (int32_t)mctx.emin;
#endif
if (t->restricted)
mpd_restrcontext(&mctx, &dctx);
/* Precisions from 1-100 */
for (mctx.prec = 1; mctx.prec <= 100; mctx.prec++) {
dctx.digits = (int32_t)mctx.prec;
if (verbose) {
printf(" prec: %"PRI_mpd_ssize_t" emin: %"PRI_mpd_ssize_t""
" emax: %"PRI_mpd_ssize_t"\n",
mctx.prec, mctx.emin, mctx.emax);
fflush(stdout);
}
for (i = 0; i < n_round; i++) {
mctx.round = mround[i];
dctx.round = dround[i];
for (mctx.clamp = 0; mctx.clamp <= 1; mctx.clamp++) {
dctx.clamp = mctx.clamp;
switch (t->type) {
case UN:
test_unary(t, mctx.prec);
break;
case BN:
test_binary(t, mctx.prec);
break;
case TN:
test_ternary(t, mctx.prec);
break;
default:
abort();
break;
}
}
}
}
}
static void
test_random(const testset *t)
{
int i, n_round;
mpd_ssize_t max_emax;
#ifdef CONFIG_64
max_emax = 999999999;
#else
max_emax = 425000000;
#endif
if (t->restricted) {
max_emax = 999999;
}
printf("testing %s ...\n", t->name);
fflush(stdout);
n_round = sizeof(mround) / sizeof(*mround);
mctx.prec = random()%100 + 1;
mctx.emax = random()%(max_emax-mctx.prec);
mctx.emin = -mctx.emax;
dctx.digits = (int32_t)mctx.prec;
dctx.emax = (int32_t)mctx.emax;
dctx.emin = (int32_t)mctx.emin;
if (verbose) {
printf(" prec: %"PRI_mpd_ssize_t" emin: %"PRI_mpd_ssize_t""
" emax: %"PRI_mpd_ssize_t"\n",
mctx.prec, mctx.emin, mctx.emax);
fflush(stdout);
}
for (i = 0; i < n_round; i++) {
mctx.round = mround[i];
dctx.round = dround[i];
for (mctx.clamp = 0; mctx.clamp <= 1; mctx.clamp++) {
dctx.clamp = mctx.clamp;
switch (t->type) {
case UN:
test_unary(t, mctx.prec);
break;
case BN:
test_binary(t, mctx.prec);
break;
case TN:
test_ternary(t, mctx.prec);
break;
default:
abort();
break;
}
}
}
}
static void
doit(void)
{
testset *t;
mpd_testcontext(&mctx, &dctx);
mpd_readcontext(&m_maxctx, &d_maxctx);
for (t = (testset *)testsets; t->id != MAXID; t++) {
if (short_tests) {
test_random(t);
}
else {
test_all(t);
}
}
}
const char *usage = "\
runtest: usage: deccheck [-v] [--short] [--minalloc] [--exit]\n";
const char *minalloc_fmt = "\n\
# ======================================================================\n\
# minalloc: %" PRI_mpd_ssize_t "\n\
# ======================================================================\n\
\n";
int main(int argc, char **argv)
{
mpd_ssize_t ma;
mpd_ssize_t limit = MPD_MINALLOC_MIN;
int i;
if (argc > 1) {
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0) {
verbose = 1;
}
else if (strcmp(argv[i], "--short") == 0) {
short_tests = 1;
}
else if (strcmp(argv[i], "--minalloc") == 0) {
limit = MPD_MINALLOC_MAX;
}
else if (strcmp(argv[i], "--exit") == 0) {
exit_on_error = 1;
}
else {
fputs(usage, stderr);
exit(EXIT_FAILURE);
}
}
}
srandom((unsigned int)time(NULL));
for (ma = MPD_MINALLOC_MIN; ma <= limit; ma++) {
/* DON'T do this in a real program. You have to be sure that
* no previously allocated decimals will ever be used again. */
MPD_MINALLOC = ma;
if (limit == MPD_MINALLOC_MAX) {
printf(minalloc_fmt, MPD_MINALLOC);
fflush(stdout);
}
mop1 = mpd_qnew();
mop2 = mpd_qnew();
mop3 = mpd_qnew();
mresult = mpd_qnew();
mtmp = mpd_qnew();
doit();
mpd_del(mop1);
mpd_del(mop2);
mpd_del(mop3);
mpd_del(mresult);
mpd_del(mtmp);
}
/* Valgrind */
mpd_del(&mpd_ln10);
fprintf(stderr, "\n");
fprintf(stderr, "counter: %20" PRIu64 "\n", counter);
fprintf(stderr, "nans: %20" PRIu64 "\n", nans);
fprintf(stderr, "infinities: %20" PRIu64 "\n", infinities);
fprintf(stderr, "subnormals: %20" PRIu64 "\n", subnormals);
fprintf(stderr, "zeros: %20" PRIu64 "\n", zeros);
fprintf(stderr, "skipped: %20" PRIu64 "\n", skipped);
fprintf(stderr, "skipped_flags: %20" PRIu64 "\n", skipped_flags);
fprintf(stderr, "\n");
return retval;
}