arb/mag.h

347 lines
7.6 KiB
C
Raw Normal View History

/*=============================================================================
This file is part of ARB.
ARB 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.
ARB 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 ARB; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
=============================================================================*/
/******************************************************************************
Copyright (C) 2014 Fredrik Johansson
******************************************************************************/
#ifndef MAG_H
#define MAG_H
#include <math.h>
#include "flint.h"
#include "arf.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
The mag_t type is an unsigned floating-point type with a fixed-precision
mantissa (30 bits) and unlimited exponent range, suited for representing
magnitude bounds efficiently.
Operations always produce a strict upper/lower bound, but for performance
reasons, no attempt is made to compute the best possible bound
(in general, a result may a few ulps larger/smaller than the optimal value).
The special values zero and positive infinity are supported (but not NaN).
Applications requiring more flexibility (such as correct rounding, or
higher precision) should use the arf_t type instead.
*/
/* TODO: arf.h should depend on this, not the other way around */
#define MAG_EXPREF(x) (&(x)->exp)
#define MAG_EXP(x) ((x)->exp)
#define MAG_MAN(x) ((x)->man)
/* Finite and with lagom big exponents. */
#define MAG_IS_LAGOM(x) (MAG_EXP(x) >= ARF_MIN_LAGOM_EXP && \
MAG_EXP(x) <= ARF_MAX_LAGOM_EXP)
#define MAG_BITS 30
#define MAG_ONE_HALF (1UL << (MAG_BITS - 1))
static __inline__ mp_limb_t
__mag_fixmul32(mp_limb_t x, mp_limb_t y)
{
mp_limb_t u, v;
umul_ppmm(u, v, x, y);
return (u << (32 - MAG_BITS)) | (v >> MAG_BITS);
}
#if FLINT_BITS == 64
#define MAG_FIXMUL(x, y) (((x) * (y)) >> MAG_BITS)
#else
#define MAG_FIXMUL(x, y) __mag_fixmul32((x), (y))
#endif
#define MAG_FAST_ADJUST_ONE_TOO_LARGE(x) \
do { \
mp_limb_t __t = MAG_MAN(x) >> MAG_BITS; \
MAG_MAN(x) = (MAG_MAN(x) >> __t) + __t; \
MAG_EXP(x) += __t; \
} while (0)
2014-05-06 19:17:29 +02:00
#define MAG_FAST_ADJUST_ONE_TOO_SMALL(x) \
do { \
mp_limb_t __t = MAG_MAN(x) >> (MAG_BITS - 1); \
MAG_MAN(x) = (MAG_MAN(x) << __t); \
MAG_EXP(x) -= __t; \
} while (0)
#define MAG_ADJUST_ONE_TOO_LARGE(x) \
do { \
mp_limb_t __t = MAG_MAN(x) >> MAG_BITS; \
MAG_MAN(x) = (MAG_MAN(x) >> __t) + __t; \
if (__t) \
fmpz_add_ui(MAG_EXPREF(x), MAG_EXPREF(x), __t); \
} while (0)
#define MAG_CHECK_BITS(rr) \
if (MAG_MAN(rr) != 0 && FLINT_BIT_COUNT(MAG_MAN(rr)) != MAG_BITS) \
{ \
printf("FAIL: wrong number of bits in mantissa!\n"); \
abort(); \
}
typedef struct
{
fmpz exp;
mp_limb_t man;
}
mag_struct;
typedef mag_struct mag_t[1];
static __inline__ void
mag_init(mag_t x)
{
fmpz_init(MAG_EXPREF(x));
MAG_MAN(x) = 0;
}
static __inline__ void
mag_clear(mag_t x)
{
fmpz_clear(MAG_EXPREF(x));
}
static __inline__ void
mag_zero(mag_t x)
{
fmpz_zero(MAG_EXPREF(x));
MAG_MAN(x) = 0;
}
static __inline__ void
mag_inf(mag_t x)
{
fmpz_clear(MAG_EXPREF(x));
MAG_EXP(x) = ARF_EXP_POS_INF;
}
static __inline__ int
mag_is_special(const mag_t x)
{
return MAG_MAN(x) == 0;
}
static __inline__ int
mag_is_zero(const mag_t x)
{
return (MAG_MAN(x) == 0) && (MAG_EXP(x) == 0);
}
static __inline__ int
mag_is_inf(const mag_t x)
{
return (MAG_MAN(x) == 0) && (MAG_EXP(x) != 0);
}
/* general versions */
static __inline__ void
mag_init_set_arf(mag_t y, const arf_t x)
{
abort();
}
static __inline__ void
mag_mul(mag_t z, const mag_t x, const mag_t y)
{
abort();
}
static __inline__ void
mag_addmul(mag_t z, const mag_t x, const mag_t y)
{
abort();
}
void mag_set_fmpr(mag_t x, const fmpr_t y);
void mag_set_arf(mag_t y, const arf_t x);
/* Fast versions (no infs/nans, small exponents). Note that this
applies to outputs too! */
static __inline__ void
mag_fast_init_set(mag_t x, const mag_t y)
{
MAG_EXP(x) = MAG_EXP(y);
MAG_MAN(x) = MAG_MAN(y);
}
static __inline__ void
mag_fast_zero(mag_t x)
{
MAG_EXP(x) = 0;
MAG_MAN(x) = 0;
}
static __inline__ int
mag_fast_is_zero(const mag_t x)
{
return MAG_MAN(x) == 0;
}
static __inline__ void
mag_fast_init_set_arf(mag_t y, const arf_t x)
{
if (ARF_IS_SPECIAL(x)) /* x == 0 */
{
mag_fast_zero(y);
}
else
{
mp_srcptr xp;
mp_size_t xn;
ARF_GET_MPN_READONLY(xp, xn, x);
MAG_MAN(y) = (xp[xn - 1] >> (FLINT_BITS - MAG_BITS)) + LIMB_ONE;
MAG_EXP(y) = ARF_EXP(x);
MAG_FAST_ADJUST_ONE_TOO_LARGE(y);
}
}
static __inline__ void
mag_fast_mul(mag_t z, const mag_t x, const mag_t y)
{
if (MAG_MAN(x) == 0 || MAG_MAN(y) == 0)
{
mag_fast_zero(z);
}
else
{
MAG_MAN(z) = MAG_FIXMUL(MAG_MAN(x), MAG_MAN(y)) + LIMB_ONE;
MAG_EXP(z) = MAG_EXP(x) + MAG_EXP(y);
2014-05-06 19:17:29 +02:00
MAG_FAST_ADJUST_ONE_TOO_SMALL(z);
}
}
static __inline__ void
mag_fast_addmul(mag_t z, const mag_t x, const mag_t y)
{
if (MAG_MAN(z) == 0)
{
mag_fast_mul(z, x, y);
}
else if (MAG_MAN(x) == 0 || MAG_MAN(y) == 0)
{
return;
}
else
{
long shift, e;
/* x*y < 2^e */
e = MAG_EXP(x) + MAG_EXP(y);
shift = MAG_EXP(z) - e;
if (shift >= 0)
{
if (shift >= MAG_BITS)
MAG_MAN(z)++;
else
MAG_MAN(z) = MAG_MAN(z) + (MAG_FIXMUL(MAG_MAN(x),
MAG_MAN(y)) >> shift) + LIMB_ONE;
}
else
{
shift = -shift;
MAG_EXP(z) = e;
if (shift >= MAG_BITS)
MAG_MAN(z) = MAG_FIXMUL(MAG_MAN(x), MAG_MAN(y))
+ (2 * LIMB_ONE);
else
MAG_MAN(z) = MAG_FIXMUL(MAG_MAN(x), MAG_MAN(y))
+ (MAG_MAN(z) >> shift) + (2 * LIMB_ONE);
}
MAG_FAST_ADJUST_ONE_TOO_LARGE(z);
}
}
static __inline__ void
mag_fast_add_2exp_si(mag_t z, const mag_t x, long e)
{
/* Must be zero */
if (mag_is_special(x))
{
MAG_MAN(z) = MAG_ONE_HALF;
MAG_EXP(z) = e + 1;
}
else
{
long shift;
shift = MAG_EXP(x) - e;
if (shift >= 0)
{
MAG_EXP(z) = MAG_EXP(x);
if (shift >= MAG_BITS)
MAG_MAN(z) = MAG_MAN(x) + LIMB_ONE;
else
MAG_MAN(z) = MAG_MAN(x) + (LIMB_ONE << (MAG_BITS - shift));
}
else
{
shift = -shift;
MAG_EXP(z) = e;
if (shift >= MAG_BITS)
MAG_MAN(z) = (LIMB_ONE << MAG_BITS) + LIMB_ONE;
else
MAG_MAN(z) = (LIMB_ONE << MAG_BITS) + (MAG_MAN(x) >> shift);
}
MAG_FAST_ADJUST_ONE_TOO_LARGE(z);
}
}
static __inline__ void
mag_get_fmpr(fmpr_t x, const mag_t r)
{
if (!MAG_IS_LAGOM(r))
abort();
fmpr_set_ui_2exp_si(x, MAG_MAN(r), MAG_EXP(r) - MAG_BITS);
}
#ifdef __cplusplus
}
#endif
#endif