mirror of
https://review.haiku-os.org/buildtools
synced 2025-02-14 17:57:39 +01:00
187 lines
6.8 KiB
C
187 lines
6.8 KiB
C
/* double_rounding.c -- Functions for checking double rounding.
|
|
|
|
Copyright (C) 2013, 2014 INRIA
|
|
|
|
This file is part of GNU MPC.
|
|
|
|
GNU MPC is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU Lesser General Public License as published by the
|
|
Free Software Foundation; either version 3 of the License, or (at your
|
|
option) any later version.
|
|
|
|
GNU MPC 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 Lesser General Public License for
|
|
more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program. If not, see http://www.gnu.org/licenses/ .
|
|
*/
|
|
|
|
#include "mpc-tests.h"
|
|
|
|
/* return 1 if double rounding occurs;
|
|
return 0 otherwise */
|
|
static int
|
|
double_rounding_mpfr (mpfr_ptr lowprec,
|
|
mpfr_ptr hiprec, int hiprec_inex, mpfr_rnd_t hiprec_rnd)
|
|
{
|
|
mpfr_exp_t hiprec_err;
|
|
mpfr_rnd_t lowprec_rnd = hiprec_rnd;
|
|
mpfr_prec_t lowprec_prec = mpfr_get_prec (lowprec);
|
|
|
|
/* hiprec error is bounded by one ulp */
|
|
hiprec_err = mpfr_get_prec (hiprec) - 1;
|
|
|
|
if (hiprec_rnd == MPFR_RNDN)
|
|
/* when rounding to nearest, use the trick for determining the
|
|
correct ternary value which is described in the MPFR
|
|
documentation */
|
|
{
|
|
hiprec_err++; /* error is bounded by one half-ulp */
|
|
lowprec_rnd = MPFR_RNDZ;
|
|
lowprec_prec++;
|
|
}
|
|
|
|
return (hiprec_inex == 0
|
|
|| mpfr_can_round (hiprec, hiprec_err, hiprec_rnd,
|
|
lowprec_rnd, lowprec_prec));
|
|
}
|
|
|
|
/* return 1 if double rounding occurs;
|
|
return 0 otherwise */
|
|
static int
|
|
double_rounding_mpc (mpc_ptr lowprec,
|
|
mpc_ptr hiprec, int hiprec_inex, mpc_rnd_t hiprec_rnd)
|
|
{
|
|
mpfr_ptr lowprec_re = mpc_realref (lowprec);
|
|
mpfr_ptr lowprec_im = mpc_imagref (lowprec);
|
|
mpfr_ptr hiprec_re = mpc_realref (hiprec);
|
|
mpfr_ptr hiprec_im = mpc_imagref (hiprec);
|
|
int inex_re = MPC_INEX_RE (hiprec_inex);
|
|
int inex_im = MPC_INEX_IM (hiprec_inex);
|
|
mpfr_rnd_t rnd_re = MPC_RND_RE (hiprec_rnd);
|
|
mpfr_rnd_t rnd_im = MPC_RND_IM (hiprec_rnd);
|
|
|
|
return (double_rounding_mpfr (lowprec_re, hiprec_re, inex_re, rnd_re)
|
|
&& double_rounding_mpfr (lowprec_im, hiprec_im, inex_im, rnd_im));
|
|
}
|
|
|
|
/* check whether double rounding occurs; if not, round extra precise output
|
|
value and set reference parameter */
|
|
int
|
|
double_rounding (mpc_fun_param_t *params)
|
|
{
|
|
int out;
|
|
const int offset = params->nbout + params->nbin;
|
|
int rnd_index = offset - params->nbrnd;
|
|
|
|
for (out = 0; out < params->nbout; out++) {
|
|
if (params->T[out] == MPC)
|
|
{
|
|
int inex;
|
|
|
|
MPC_ASSERT ((params->T[0] == MPC_INEX)
|
|
|| (params->T[0] == MPCC_INEX));
|
|
MPC_ASSERT ((params->T[offset] == MPC_INEX)
|
|
|| (params->T[offset] == MPCC_INEX));
|
|
MPC_ASSERT (params->T[out + offset] == MPC);
|
|
MPC_ASSERT (params->T[rnd_index] == MPC_RND);
|
|
|
|
/*
|
|
For the time being, there may be either one or two rounding modes;
|
|
in the latter case, we assume that there are three outputs:
|
|
the inexact value and two complex numbers.
|
|
*/
|
|
inex = (params->nbrnd == 1 ? params->P[0].mpc_inex
|
|
: (out == 1 ? MPC_INEX1 (params->P[0].mpcc_inex)
|
|
: MPC_INEX2 (params->P[0].mpcc_inex)));
|
|
|
|
if (double_rounding_mpc (params->P[out + offset].mpc_data.mpc,
|
|
params->P[out].mpc,
|
|
inex,
|
|
params->P[rnd_index].mpc_rnd))
|
|
/* the high-precision value and the exact value round to the same
|
|
low-precision value */
|
|
{
|
|
int inex_hp, inex_re, inex_im;
|
|
inex = mpc_set (params->P[out + offset].mpc_data.mpc,
|
|
params->P[out].mpc,
|
|
params->P[rnd_index].mpc_rnd);
|
|
params->P[out + offset].mpc_data.known_sign_real = -1;
|
|
params->P[out + offset].mpc_data.known_sign_imag = -1;
|
|
|
|
/* no double rounding means that the ternary value may come from
|
|
the high-precision calculation or from the rounding */
|
|
if (params->nbrnd == 1)
|
|
inex_hp = params->P[0].mpc_inex;
|
|
else /* nbrnd == 2 */
|
|
if (out == 1)
|
|
inex_hp = MPC_INEX1 (params->P[0].mpcc_inex);
|
|
else /* out == 2 */
|
|
inex_hp = MPC_INEX2 (params->P[0].mpcc_inex);
|
|
|
|
if (MPC_INEX_RE (inex) == 0)
|
|
inex_re = MPC_INEX_RE (inex_hp);
|
|
else
|
|
inex_re = MPC_INEX_RE (inex);
|
|
if (MPC_INEX_IM (inex) == 0)
|
|
inex_im = MPC_INEX_IM (inex_hp);
|
|
else
|
|
inex_im = MPC_INEX_IM (inex);
|
|
|
|
if (params->nbrnd == 1) {
|
|
params->P[offset].mpc_inex_data.real = inex_re;
|
|
params->P[offset].mpc_inex_data.imag = inex_im;
|
|
}
|
|
else /* nbrnd == 2 */
|
|
if (out == 1)
|
|
params->P[offset].mpcc_inex = MPC_INEX (inex_re, inex_im);
|
|
else /* out == 2 */
|
|
params->P[offset].mpcc_inex
|
|
= MPC_INEX12 (params->P[offset].mpcc_inex,
|
|
MPC_INEX (inex_re, inex_im));
|
|
|
|
rnd_index++;
|
|
}
|
|
else
|
|
/* double rounding occurs */
|
|
return 1;
|
|
}
|
|
else if (params->T[out] == MPFR)
|
|
{
|
|
MPC_ASSERT (params->T[0] == MPFR_INEX);
|
|
MPC_ASSERT (params->T[offset] == MPFR_INEX);
|
|
MPC_ASSERT (params->T[out + offset] == MPFR);
|
|
MPC_ASSERT (params->T[rnd_index] == MPFR_RND);
|
|
|
|
if (double_rounding_mpfr (params->P[out + offset].mpfr_data.mpfr,
|
|
params->P[out].mpfr,
|
|
params->P[0].mpfr_inex,
|
|
params->P[rnd_index].mpfr_rnd))
|
|
/* the hight-precision value and the exact value round to the same
|
|
low-precision value */
|
|
{
|
|
int inex;
|
|
inex = mpfr_set (params->P[out + offset].mpfr_data.mpfr,
|
|
params->P[out].mpfr,
|
|
params->P[rnd_index].mpfr_rnd);
|
|
params->P[out + offset].mpfr_data.known_sign = -1;
|
|
|
|
/* no double rounding means that the ternary value may comes from
|
|
the high-precision calculation or from the rounding */
|
|
if (inex == 0)
|
|
params->P[offset].mpfr_inex = params->P[0].mpfr_inex;
|
|
else
|
|
params->P[offset].mpfr_inex = inex;
|
|
|
|
rnd_index++;
|
|
}
|
|
else
|
|
/* double rounding occurs */
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|