/* Copyright (C) 2016 William Hart Copyright (C) 2021 Fredrik Johansson This file is part of FLINT. FLINT is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See . */ #include #include #include "flint.h" #include "fmpz.h" #include "fmpz_poly.h" /* Naive double+exponent arithmetic; not designed to deal with underflow/overflow. */ typedef struct { double m; slong e; } dpe_t; /* Do up to ADJUSTMENT_DELAY steps without adjusting mantissa. This may cause the mantissa to drift from the normalized range [0.5, 1) by a factor about 2^ADJUSTMENT_DELAY. */ #define ADJUSTMENT_DELAY 16 /* Standardize nonzero mantissa to +/- [0.5, 1). */ #define DPE_ADJUST(x) \ do { \ int _e; \ (x).m = frexp((x).m, &_e); \ (x).e += _e; \ } while (0) static dpe_t dpe_set_d_exp(double x, slong e) { dpe_t res; res.m = x; res.e = e; DPE_ADJUST(res); return res; } static dpe_t dpe_set_fmpz(const fmpz_t x) { dpe_t res; res.m = fmpz_get_d_2exp(&res.e, x); return res; } static dpe_t dpe_add(dpe_t x, dpe_t y) { dpe_t res; slong d; d = x.e - y.e; if (x.m == 0.0) return y; if (y.m == 0.0) return x; if (d >= 0) { if (d > 53 + ADJUSTMENT_DELAY) return x; res.m = x.m + ldexp(y.m, -d); res.e = x.e; } else { d = -d; if (d > 53 + ADJUSTMENT_DELAY) return y; res.m = y.m + ldexp(x.m, -d); res.e = y.e; } /* We delay adjustments */ /* DPE_ADJUST(res); */ return res; } static dpe_t dpe_mul(dpe_t x, dpe_t y) { dpe_t res; res.m = x.m * y.m; res.e = x.e + y.e; /* We delay adjustments */ /* DPE_ADJUST(res); */ return res; } double _fmpz_poly_evaluate_horner_d_2exp2(slong * exp, const fmpz * poly, slong n, double d, slong dexp) { dpe_t s, t, x; slong i; if (d == 0.0) return fmpz_get_d_2exp(exp, poly + 0); x = dpe_set_d_exp(d, dexp); s = dpe_set_fmpz(poly + n - 1); for (i = n - 2; i >= 0; i--) { s = dpe_mul(s, x); if (!fmpz_is_zero(poly + i)) { t = dpe_set_fmpz(poly + i); s = dpe_add(s, t); } /* Delayed adjustments. */ if (i % ADJUSTMENT_DELAY == 0) DPE_ADJUST(s); } DPE_ADJUST(s); *exp = s.e; return s.m; } double _fmpz_poly_evaluate_horner_d_2exp(slong * exp, const fmpz * poly, slong n, double d) { return _fmpz_poly_evaluate_horner_d_2exp2(exp, poly, n, d, 0); } double fmpz_poly_evaluate_horner_d_2exp2(slong * exp, const fmpz_poly_t poly, double d, slong dexp) { if (poly->length == 0) { *exp = 0; return 0.0; } return _fmpz_poly_evaluate_horner_d_2exp2(exp, poly->coeffs, poly->length, d, dexp); } double fmpz_poly_evaluate_horner_d_2exp(slong * exp, const fmpz_poly_t poly, double d) { if (poly->length == 0) { *exp = 0; return 0.0; } return _fmpz_poly_evaluate_horner_d_2exp(exp, poly->coeffs, poly->length, d); }