mp-quad.c

     
   1  //! @file mp-quad.c
   2  //! @author J. Marcel van der Veer
   3  //!
   4  //! @section Copyright
   5  //!
   6  //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
   7  //! Copyright 2001-2023 J. Marcel van der Veer [algol68g@xs4all.nl].
   8  //!
   9  //! @section License
  10  //!
  11  //! This program is free software; you can redistribute it and/or modify it 
  12  //! under the terms of the GNU General Public License as published by the 
  13  //! Free Software Foundation; either version 3 of the License, or 
  14  //! (at your option) any later version.
  15  //!
  16  //! This program is distributed in the hope that it will be useful, but 
  17  //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  18  //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
  19  //! more details. You should have received a copy of the GNU General Public 
  20  //! License along with this program. If not, see [http://www.gnu.org/licenses/].
  21  
  22  //! @section Synopsis
  23  //!
  24  //! Fixed-length LONG LONG REAL and COMPLEX routines.
  25  
  26  #include "a68g.h"
  27  
  28  #if (A68_LEVEL >= 3)
  29  
  30  #include "a68g-genie.h"
  31  #include "a68g-prelude.h"
  32  #include "a68g-mp.h"
  33  #include "a68g-double.h"
  34  #include "a68g-quad.h"
  35  
  36  //! @brief Convert mp to quad real.
  37  
  38  QUAD_T mp_to_quad_real (NODE_T * p, MP_T * z, int digits)
  39  {
  40  // This routine looks a lot like "strtod".
  41    (void) p;
  42    QUAD_T u = QUAD_REAL_ZERO;
  43    if (MP_EXPONENT (z) * (MP_T) LOG_MP_RADIX > (MP_T) REAL_MIN_10_EXP) {
  44      BOOL_T neg = MP_DIGIT (z, 1) < 0;
  45      MP_DIGIT (z, 1) = ABS (MP_DIGIT (z, 1));
  46      int expo = (int) (MP_EXPONENT (z) * LOG_MP_RADIX);
  47      QUAD_T w = ten_up_quad_real (expo), r = double_real_to_quad_real (MP_RADIX);
  48      for (int j = 1; j <= digits; j++) {
  49        QUAD_T term = double_real_to_quad_real (MP_DIGIT (z, j));
  50        term = mul_quad_real (term, w);
  51        u = _add_quad_real_ (u, term);
  52        w = div_quad_real (w, r);
  53      }
  54      if (neg) {
  55        u = neg_quad_real (u);
  56      }
  57    }
  58    return u;
  59  }
  60  
  61  //! @brief Convert quad real to mp number.
  62  
  63  void quad_real_to_mp (NODE_T * p, MP_T *z, QUAD_T x, int digits)
  64  {
  65    SET_MP_ZERO (z, digits);
  66    if (is0_quad_real (&x)) {
  67      return;
  68    }
  69    int sign_x = getsgn_quad_real (&x);
  70  // Scale to [0, 0.1>.
  71  // a = ABS (x);
  72    QUAD_T u = abs_quad_real (x);
  73  // expo = (int) log10q (a);
  74    QUAD_T v = log10_quad_real (u);
  75    INT_T expo = (INT_T) quad_real_to_double_real (v);
  76  // v /= ten_up_double (expo);
  77    v = ten_up_quad_real (expo);
  78    u = div_quad_real (u, v);
  79    expo--;
  80    if (real_cmp_quad_real (&u, &QUAD_REAL_ONE) >= 0) {
  81      u = div_quad_real (u, QUAD_REAL_TEN);
  82      expo++;
  83    }
  84  // Transport digits of x to the mantissa of z.
  85    INT_T sum = 0, W = (MP_RADIX / 10); int j, k;
  86    for (k = 0, j = 1; j <= digits && k < QUAD_DIGITS; k++) {
  87      QUAD_T t = mul_quad_real (u, QUAD_REAL_TEN);
  88      v = floor_quad_real (t);
  89      u = frac_quad_real (t);
  90      sum += (INT_T) (W * quad_real_to_double_real (v));
  91      W /= 10;
  92      if (W < 1) {
  93        MP_DIGIT (z, j++) = (MP_T) sum;
  94        sum = 0;
  95        W = (MP_RADIX / 10);
  96      }
  97    }
  98  // Store the last digits.
  99    if (j <= digits) {
 100      MP_DIGIT (z, j) = (MP_T) sum;
 101    }
 102    (void) align_mp (z, &expo, digits);
 103    MP_EXPONENT (z) = (MP_T) expo;
 104    if (sign_x != 0) {
 105      MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
 106    }
 107    check_mp_exp (p, z);
 108  }
 109  
 110  //! @brief PROC quad mp = (LONG LONG REAL) LONG LONG REAL
 111  
 112  void genie_quad_mp (NODE_T * p)
 113  {
 114    MOID_T *mode = MOID (p);
 115    int digits = DIGITS (mode);
 116    int size = SIZE (mode);
 117    MP_T *z = (MP_T *) STACK_OFFSET (-size);
 118    QUAD_T u = mp_to_quad_real (p, z, digits);
 119    quad_real_to_mp (p, z, u, digits);
 120  }
 121  
 122  #endif