/* Definitions for simple data type for real numbers. Copyright (C) 2002-2024 Free Software Foundation, Inc. This file is part of GCC. GCC 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 3, or (at your option) any later version. GCC 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 GCC; see the file COPYING3. If not see . */ #ifndef GCC_SREAL_H #define GCC_SREAL_H #define SREAL_PART_BITS 31 #define UINT64_BITS 64 #define SREAL_MIN_SIG ((int64_t) 1 << (SREAL_PART_BITS - 2)) #define SREAL_MAX_SIG (((int64_t) 1 << (SREAL_PART_BITS - 1)) - 1) #define SREAL_MAX_EXP (INT_MAX / 4) #define SREAL_BITS SREAL_PART_BITS #define SREAL_SIGN(v) (v < 0 ? -1 : 1) #define SREAL_ABS(v) (v < 0 ? -v : v) struct output_block; class lto_input_block; /* Structure for holding a simple real number. */ class sreal { public: /* Construct an uninitialized sreal. */ sreal () : m_sig (-1), m_exp (-1) {} /* Construct a sreal. */ sreal (int64_t sig, int exp = 0) { normalize (sig, exp); } void dump (FILE *) const; int64_t to_int () const; int64_t to_nearest_int () const; double to_double () const; void stream_out (struct output_block *); static sreal stream_in (class lto_input_block *); sreal operator+ (const sreal &other) const; sreal operator- (const sreal &other) const; sreal operator* (const sreal &other) const; sreal operator/ (const sreal &other) const; bool operator< (const sreal &other) const { if (m_exp == other.m_exp) return m_sig < other.m_sig; else { bool negative = m_sig < 0; bool other_negative = other.m_sig < 0; if (negative != other_negative) return negative > other_negative; bool r = m_exp < other.m_exp; return negative ? !r : r; } } bool operator== (const sreal &other) const { return m_exp == other.m_exp && m_sig == other.m_sig; } sreal operator- () const { sreal tmp = *this; tmp.m_sig *= -1; return tmp; } sreal shift (int s) const { /* Zero needs no shifting. */ if (!m_sig) return *this; gcc_checking_assert (s <= SREAL_MAX_EXP); gcc_checking_assert (s >= -SREAL_MAX_EXP); /* Overflows/drop to 0 could be handled gracefully, but hopefully we do not need to do so. */ gcc_checking_assert (m_exp + s <= SREAL_MAX_EXP); gcc_checking_assert (m_exp + s >= -SREAL_MAX_EXP); sreal tmp = *this; tmp.m_exp += s; return tmp; } /* Global minimum sreal can hold. */ inline static sreal min () { sreal min; /* This never needs normalization. */ min.m_sig = -SREAL_MAX_SIG; min.m_exp = SREAL_MAX_EXP; return min; } /* Global minimum sreal can hold. */ inline static sreal max () { sreal max; /* This never needs normalization. */ max.m_sig = SREAL_MAX_SIG; max.m_exp = SREAL_MAX_EXP; return max; } private: inline void normalize (int64_t new_sig, signed int new_exp); inline void normalize_up (int64_t new_sig, signed int new_exp); inline void normalize_down (int64_t new_sig, signed int new_exp); void shift_right (int amount); static sreal signedless_plus (const sreal &a, const sreal &b, bool negative); static sreal signedless_minus (const sreal &a, const sreal &b, bool negative); int32_t m_sig; /* Significant. */ signed int m_exp; /* Exponent. */ }; extern void debug (const sreal &ref); extern void debug (const sreal *ptr); inline sreal &operator+= (sreal &a, const sreal &b) { return a = a + b; } inline sreal &operator-= (sreal &a, const sreal &b) { return a = a - b; } inline sreal &operator/= (sreal &a, const sreal &b) { return a = a / b; } inline sreal &operator*= (sreal &a, const sreal &b) { return a = a * b; } inline bool operator!= (const sreal &a, const sreal &b) { return !(a == b); } inline bool operator> (const sreal &a, const sreal &b) { return !(a == b || a < b); } inline bool operator<= (const sreal &a, const sreal &b) { return a < b || a == b; } inline bool operator>= (const sreal &a, const sreal &b) { return a == b || a > b; } inline sreal operator<< (const sreal &a, int exp) { return a.shift (exp); } inline sreal operator>> (const sreal &a, int exp) { return a.shift (-exp); } /* Make significant to be >= SREAL_MIN_SIG. Make this separate method so inliner can handle hot path better. */ inline void sreal::normalize_up (int64_t new_sig, signed int new_exp) { unsigned HOST_WIDE_INT sig = absu_hwi (new_sig); int shift = SREAL_PART_BITS - 2 - floor_log2 (sig); gcc_checking_assert (shift > 0); sig <<= shift; new_exp -= shift; gcc_checking_assert (sig <= SREAL_MAX_SIG && sig >= SREAL_MIN_SIG); /* Check underflow. */ if (new_exp < -SREAL_MAX_EXP) { new_exp = -SREAL_MAX_EXP; sig = 0; } m_exp = new_exp; if (SREAL_SIGN (new_sig) == -1) m_sig = -sig; else m_sig = sig; } /* Make significant to be <= SREAL_MAX_SIG. Make this separate method so inliner can handle hot path better. */ inline void sreal::normalize_down (int64_t new_sig, signed int new_exp) { int last_bit; unsigned HOST_WIDE_INT sig = absu_hwi (new_sig); int shift = floor_log2 (sig) - SREAL_PART_BITS + 2; gcc_checking_assert (shift > 0); last_bit = (sig >> (shift-1)) & 1; sig >>= shift; new_exp += shift; gcc_checking_assert (sig <= SREAL_MAX_SIG && sig >= SREAL_MIN_SIG); /* Round the number. */ sig += last_bit; if (sig > SREAL_MAX_SIG) { sig >>= 1; new_exp++; } /* Check overflow. */ if (new_exp > SREAL_MAX_EXP) { new_exp = SREAL_MAX_EXP; sig = SREAL_MAX_SIG; } m_exp = new_exp; if (SREAL_SIGN (new_sig) == -1) m_sig = -sig; else m_sig = sig; } /* Normalize *this; the hot path. */ inline void sreal::normalize (int64_t new_sig, signed int new_exp) { unsigned HOST_WIDE_INT sig = absu_hwi (new_sig); if (sig == 0) { m_sig = 0; m_exp = -SREAL_MAX_EXP; } else if (sig > SREAL_MAX_SIG) normalize_down (new_sig, new_exp); else if (sig < SREAL_MIN_SIG) normalize_up (new_sig, new_exp); else { m_sig = new_sig; m_exp = new_exp; } } #endif