/*++ Copyright (c) 2011 Microsoft Corporation Module Name: mpf.h Abstract: Multi Precision Floating Point Numbers Author: Christoph Wintersteiger (cwinter) 2011-12-01. Revision History: --*/ #pragma once #include #include "util/mpz.h" #include "util/mpq.h" #include "util/map.h" #include "util/scoped_numeral.h" #include "util/scoped_numeral_vector.h" #include "util/hash.h" typedef enum { MPF_ROUND_NEAREST_TEVEN, MPF_ROUND_NEAREST_TAWAY, MPF_ROUND_TOWARD_POSITIVE, MPF_ROUND_TOWARD_NEGATIVE, MPF_ROUND_TOWARD_ZERO } mpf_rounding_mode; typedef int64_t mpf_exp_t; class mpf { friend class mpf_manager; friend class scoped_mpf; unsigned ebits:15; unsigned sbits:16; unsigned sign:1; // counts as one sbit. mpz significand; mpf_exp_t exponent; void set(unsigned _ebits, unsigned _sbits); public: mpf(); mpf(unsigned ebits, unsigned sbits); mpf(mpf &&) = default; ~mpf(); mpf & operator=(mpf const & other) = delete; unsigned get_ebits() const { return ebits; } unsigned get_sbits() const { return sbits; } void swap(mpf & other); }; class mpf_manager { unsynch_mpq_manager m_mpq_manager; unsynch_mpz_manager & m_mpz_manager; // A mpq_manager is a mpz_manager, reusing it. public: typedef mpf numeral; mpf_manager(); ~mpf_manager(); void reset(mpf & o, unsigned ebits, unsigned sbits) { set(o, ebits, sbits, 0); } void set(mpf & o, unsigned ebits, unsigned sbits, int value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, int n, int d); void set(mpf & o, unsigned ebits, unsigned sbits, float value); void set(mpf & o, unsigned ebits, unsigned sbits, double value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, char const * value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpz const & exponent, mpq const & significand); void set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exponent, uint64_t significand); void set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exponent, mpz const & significand); void set(mpf & o, mpf const & x); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpf const & x); void del(mpf & x) { m_mpz_manager.del(x.significand); } void abs(mpf & o); void abs(mpf const & x, mpf & o); void neg(mpf & o); void neg(mpf const & x, mpf & o); void swap(mpf& a, mpf& b) { a.swap(b); } bool is_zero(mpf const & x); bool is_neg(mpf const & x); bool is_pos(mpf const & x); bool is_one(mpf const & x); bool is_nzero(mpf const & x); bool is_pzero(mpf const & x); // structural eq bool eq_core(mpf const & x, mpf const & y) { return x.ebits == y.ebits && x.sbits == y.sbits && x.sign == y.sign && m_mpz_manager.eq(x.significand, y.significand) && x.exponent == y.exponent; } bool eq(mpf const & x, mpf const & y); bool lt(mpf const & x, mpf const & y); bool lte(mpf const & x, mpf const & y); bool le(mpf const & x, mpf const & y) { return lte(x, y); } bool gt(mpf const & x, mpf const & y); bool gte(mpf const & x, mpf const & y); bool ge(mpf const & x, mpf const & y) { return gte(x, y); } void add(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o); void sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o); void mul(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o); void div(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o); void fma(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf const &z, mpf & o); void sqrt(mpf_rounding_mode rm, mpf const & x, mpf & o); void round_to_integral(mpf_rounding_mode rm, mpf const & x, mpf & o); void rem(mpf const & x, mpf const & y, mpf & o); void maximum(mpf const & x, mpf const & y, mpf & o); void minimum(mpf const & x, mpf const & y, mpf & o); std::string to_string(mpf const & a); std::string to_rational_string(mpf const & a); void display_decimal(std::ostream & out, mpf const & a, unsigned k); void display_smt2(std::ostream & out, mpf const & a, bool decimal); // Convert x into a mpq numeral. zm is the manager that owns o. void to_rational(mpf const & x, unsynch_mpq_manager & qm, mpq & o); void to_rational(mpf const & x, scoped_mpq & o) { to_rational(x, o.m(), o); } double to_double(mpf const & x); float to_float(mpf const & x); bool sgn(mpf const & x) const { return x.sign; } const mpz & sig(mpf const & x) const { return x.significand; } void sig_normalized(mpf const & x, mpz & res) { mpf t; set(t, x); unpack(t, true); mpz_manager().set(res, t.significand); del(t); } const mpf_exp_t & exp(mpf const & x) const { return x.exponent; } mpf_exp_t exp_normalized(mpf const & x) { if (is_zero(x)) return 0; else { mpf t; set(t, x); unpack(t, true); mpf_exp_t r = t.exponent; del(t); return r; } } bool is_nan(mpf const & x); bool is_inf(mpf const & x); bool is_pinf(mpf const & x); bool is_ninf(mpf const & x); bool is_normal(mpf const & x); bool is_denormal(mpf const & x); bool is_regular(mpf const & x) { return x.sbits == 0 || is_normal(x) || is_denormal(x); } bool is_int(mpf const & x); void mk_zero(unsigned ebits, unsigned sbits, bool sign, mpf & o); void mk_nzero(unsigned ebits, unsigned sbits, mpf & o); void mk_pzero(unsigned ebits, unsigned sbits, mpf & o); void mk_nan(unsigned ebits, unsigned sbits, mpf & o); void mk_inf(unsigned ebits, unsigned sbits, bool sign, mpf & o); void mk_pinf(unsigned ebits, unsigned sbits, mpf & o); void mk_ninf(unsigned ebits, unsigned sbits, mpf & o); unsynch_mpz_manager & mpz_manager() { return m_mpz_manager; } unsynch_mpq_manager & mpq_manager() { return m_mpq_manager; } unsigned hash(mpf const & a) { return hash_u_u(m_mpz_manager.hash(a.significand), m_mpz_manager.hash(hash_ull(a.exponent))); } void mk_max_value(unsigned ebits, unsigned sbits, bool sign, mpf & o); mpf_exp_t mk_bot_exp(unsigned ebits); mpf_exp_t mk_top_exp(unsigned ebits); mpf_exp_t mk_max_exp(unsigned ebits); mpf_exp_t mk_min_exp(unsigned ebits); mpf_exp_t bias_exp(unsigned ebits, mpf_exp_t unbiased_exponent); mpf_exp_t unbias_exp(unsigned ebits, mpf_exp_t biased_exponent); /** \brief Return the biggest k s.t. 2^k <= a. \remark Return 0 if a is not positive. */ unsigned prev_power_of_two(mpf const & a); void to_sbv_mpq(mpf_rounding_mode rm, const mpf & x, scoped_mpq & o); void to_ieee_bv_mpz(const mpf & x, scoped_mpz & o); protected: void mk_one(unsigned ebits, unsigned sbits, bool sign, mpf & o) const; bool has_bot_exp(mpf const & x); bool has_top_exp(mpf const & x); void unpack(mpf & o, bool normalize); void add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & o, bool sub); void round(mpf_rounding_mode rm, mpf & o); void round_sqrt(mpf_rounding_mode rm, mpf & o); void renormalize(unsigned ebits, unsigned sbits, mpf_exp_t & exp, mpz & sig); void partial_remainder(mpf & x, mpf const & y, mpf_exp_t const & exp_diff, bool partial); void mk_round_inf(mpf_rounding_mode rm, mpf & o); // Convert x into a mpz numeral. zm is the manager that owns o. void to_mpz(mpf const & x, unsynch_mpz_manager & zm, mpz & o); void to_mpz(mpf const & x, scoped_mpz & o) { to_mpz(x, o.m(), o); } class powers2 { unsynch_mpz_manager & m; u_map m_p; u_map m_pn; u_map m_pm1; u_map m_pm1n; public: powers2(unsynch_mpz_manager & m) : m(m) {} ~powers2() { dispose(m_p); dispose(m_pn); dispose(m_pm1); dispose(m_pm1n); } void dispose(u_map & map) { for (u_map::iterator it = map.begin(); it != map.end(); it++) { m.del(*it->m_value); dealloc(it->m_value); } } const mpz & operator()(unsigned n, bool negated = false) { u_map & map = (negated) ? m_pn : m_p; u_map::iterator it = map.find_iterator(n); if (it != map.end()) return *it->m_value; else { mpz * new_obj = alloc(mpz); map.insert(n, new_obj); m.power(unsynch_mpz_manager::mk_z(2), n, *new_obj); if (negated) m.neg(*new_obj); return *new_obj; } } const mpz & m1(unsigned n, bool negated=false) { // (2 ^ n) - 1 u_map & map = (negated) ? m_pm1n : m_pm1; u_map::iterator it = map.find_iterator(n); if (it != map.end()) return *it->m_value; else { mpz * new_obj = alloc(mpz); map.insert(n, new_obj); m.power(unsynch_mpz_manager::mk_z(2), n, *new_obj); m.dec(*new_obj); if (negated) m.neg(*new_obj); return *new_obj; } } }; std::string to_string_raw(mpf const & a); std::string to_string_hexfloat(mpf const & a); std::string to_string_hexfloat(bool sgn, mpf_exp_t exp, scoped_mpz const & sig, unsigned ebits, unsigned sbits, unsigned rbits); std::string to_string_binary(mpf const & x, unsigned upper_extra, unsigned lower_extra); public: powers2 m_powers2; }; class scoped_mpf : public _scoped_numeral { friend class mpf_manager; mpz & significand() { return get().significand; } const mpz & significand() const { return get().significand; } bool sign() const { return get().sign; } mpf_exp_t exponent() const { return get().exponent; } unsigned sbits() const { return get().sbits; } void set(unsigned ebits, unsigned sbits) { get().set(ebits, sbits); } void set(unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exp, mpz & significand) { get().set(ebits, sbits); get().exponent = exp; get().sign = sign; if (&get().significand != &significand) m().mpz_manager().set(get().significand, significand); } void set(unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exp) { get().set(ebits, sbits); get().exponent = exp; get().sign = sign; m().mpz_manager().set(get().significand, 0); } public: scoped_mpf(mpf_manager & m):_scoped_numeral(m) {} scoped_mpf(scoped_mpf const & n):_scoped_numeral(n) {} scoped_mpf(mpf_manager & m, unsigned ebits, unsigned sbits):_scoped_numeral(m) { set(ebits, sbits); } }; typedef _scoped_numeral_vector scoped_mpf_vector;