rts-conversion.c

     
   1  //! @file rts-conversion.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-2026 J. Marcel van der Veer [algol68g@algol68genie.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  //! Conversion related routines.
  25  
  26  #include "a68g.h"
  27  #include "a68g-conversion.h"
  28  #include "a68g-genie.h"
  29  #include "a68g-prelude.h"
  30  #include "a68g-mp.h"
  31  #include "a68g-double.h"
  32  #include "a68g-transput.h"
  33  
  34  
  35  //! @brief 10 ** expo
  36  
  37  REAL_T ten_up (int expo)
  38  {
  39    // Values for 10 ^ (2 ^ n) for conversion purposes on IEEE 754 platforms.
  40    static REAL_T pow_10[] = {
  41      10.0, 100.0, 1.0e4, 1.0e8, 1.0e16, 1.0e32, 1.0e64, 1.0e128, 1.0e256
  42    };
  43    BOOL_T neg_expo = (BOOL_T) (expo < 0);
  44    if (neg_expo) {
  45      expo = -expo;
  46    }
  47    PRELUDE_ERROR (expo > MAX_REAL_EXPO, NO_NODE, ERROR_INVALID_VALUE, M_REAL);
  48    // This appears sufficiently accurate.
  49    REAL_T dbl_expo = 1.0;
  50    for (REAL_T *dep = pow_10; expo != 0; expo >>= 1, dep++) {
  51      if (expo & 0x1) {
  52        dbl_expo *= *dep;
  53      }
  54    }
  55    return neg_expo ? 1 / dbl_expo : dbl_expo;
  56  }
  57  
  58  
  59  //! @brief Transform string into real-8.
  60  
  61  REAL_T string_to_real (NODE_T *p, char *s)
  62  {
  63    errno = 0;
  64    #if defined (PRECISE_CONVERSIONS)
  65      return (REAL_T) string_to_double (p, s);
  66    #else
  67      (void) p;
  68      return strtod (s, NO_REF);
  69    #endif
  70  }
  71  
  72  #if (A68G_LEVEL >= 3)
  73  
  74  
  75  //! @brief 10 ** expo
  76  
  77  DOUBLE_T ten_up_double (int expo)
  78  {
  79    static DOUBLE_T pow_10_double[] = {
  80      10.0q,     100.0q,    1.0e4q,   1.0e8q,   1.0e16q, 
  81      1.0e32q,   1.0e64q,   1.0e128q, 1.0e256q, 1.0e512q, 
  82      1.0e1024q, 1.0e2048q, 1.0e4096q
  83    };
  84    // This appears sufficiently accurate.
  85    if (expo == 0) {
  86      return 1.0q;
  87    }
  88    BOOL_T neg_expo = (BOOL_T) (expo < 0);
  89    if (neg_expo) {
  90      expo = -expo;
  91    }
  92    if (expo > MAX_DOUBLE_EXPO) {
  93      expo = 0;
  94      errno = EDOM;
  95    }
  96    PRELUDE_ERROR (expo > MAX_DOUBLE_EXPO, NO_NODE, ERROR_INVALID_VALUE, M_LONG_REAL);
  97    DOUBLE_T dbl_expo = 1.0q;
  98    for (DOUBLE_T *dep = pow_10_double; expo != 0; expo >>= 1, dep++) {
  99      if (expo & 0x1) {
 100        dbl_expo *= *dep;
 101      }
 102    }
 103    return neg_expo ? 1.0q / dbl_expo : dbl_expo;
 104  }
 105  
 106  DOUBLE_NUM_T double_int_to_double (NODE_T * p, DOUBLE_NUM_T z)
 107  {
 108    int neg = D_NEG (z);
 109    if (neg) {
 110      z = abs_double_int (z);
 111    }
 112    DOUBLE_NUM_T w, radix;
 113    w.f = 0.0q;
 114    set_lw (radix, RADIX);
 115    DOUBLE_T weight = 1.0q;
 116    while (!D_ZERO (z)) {
 117      DOUBLE_NUM_T digit;
 118      digit = double_udiv (p, M_LONG_INT, z, radix, 1);
 119      w.f = w.f + LW (digit) * weight;
 120      z = double_udiv (p, M_LONG_INT, z, radix, 0);
 121      weight = weight * RADIX_Q;
 122    }
 123    if (neg) {
 124      w.f = -w.f;
 125    }
 126    return w;
 127  }
 128  
 129  DOUBLE_NUM_T double_to_double_int (NODE_T * p, DOUBLE_NUM_T z)
 130  {
 131    // This routines looks a lot like "strtol". 
 132    BOOL_T negative = (BOOL_T) (z.f < 0);
 133    z.f = fabs_double (trunc_double (z.f));
 134    if (z.f > CONST_2_UP_112_Q) {
 135      errno = EDOM;
 136      PRELUDE_ERROR (errno != 0, p, ERROR_MATH, M_LONG_REAL);
 137    }
 138    DOUBLE_NUM_T sum, weight, radix;
 139    set_lw (sum, 0);
 140    set_lw (weight, 1);
 141    set_lw (radix, RADIX);
 142    while (z.f > 0) {
 143      DOUBLE_NUM_T term, digit, quot, rest;
 144      quot.f = trunc_double (z.f / RADIX_Q);
 145      rest.f = z.f - quot.f * RADIX_Q;
 146      z.f = quot.f;
 147      set_lw (digit, (INT_T) (rest.f));
 148      term = double_umul (p, M_LONG_INT, digit, weight);
 149      sum = double_uadd (p, M_LONG_INT, sum, term);
 150      if (z.f > 0.0q) {
 151        weight = double_umul (p, M_LONG_INT, weight, radix);
 152      }
 153    }
 154    if (negative) {
 155      return neg_double_int (sum);
 156    } else {
 157      return sum;
 158    }
 159  }
 160  
 161  
 162  //! @brief LONG BITS value of LONG BITS denotation
 163  
 164  DOUBLE_NUM_T string_to_double_bits (NODE_T * p, char *s)
 165  {
 166    errno = 0;
 167    char *radix = NO_TEXT;
 168    int base = (int) a68g_strtou (s, &radix, 10);
 169    if (base < 2 || base > 16) {
 170      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_INVALID_RADIX, base);
 171      exit_genie (p, A68G_RUNTIME_ERROR);
 172    }
 173    DOUBLE_NUM_T z;
 174    set_lw (z, 0x0);
 175    if (radix != NO_TEXT && TO_UPPER (radix[0]) == TO_UPPER (RADIX_CHAR) && errno == 0) {
 176      DOUBLE_NUM_T w;
 177      char *q = radix;
 178      while (q[0] != NULL_CHAR) {
 179        q++;
 180      }
 181      set_lw (w, 1);
 182      while ((--q) != radix) {
 183        int digit = char_value (q[0]);
 184        if (digit < 0 && digit >= base) {
 185          diagnostic (A68G_RUNTIME_ERROR, p, ERROR_IN_DENOTATION, M_LONG_BITS);
 186          exit_genie (p, A68G_RUNTIME_ERROR);
 187        } else {
 188          DOUBLE_NUM_T v;
 189          set_lw (v, digit);
 190          v = double_umul (p, M_LONG_INT, v, w);
 191          z = double_uadd (p, M_LONG_INT, z, v);
 192          set_lw (v, base);
 193          w = double_umul (p, M_LONG_INT, w, v);
 194        }
 195      }
 196    } else {
 197      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_IN_DENOTATION, M_LONG_BITS);
 198      exit_genie (p, A68G_RUNTIME_ERROR);
 199    }
 200    return (z);
 201  }
 202  
 203  
 204  //! @brief Value of LONG INT denotation
 205  
 206  BOOL_T string_to_double_int (NODE_T * p, A68G_LONG_INT * z, char *s)
 207  {
 208    while (IS_SPACE (s[0])) {
 209      s++;
 210    }
 211    // Get the sign
 212    int sign = (s[0] == '-' ? -1 : 1);
 213    if (s[0] == '+' || s[0] == '-') {
 214      s++;
 215    }
 216    int end = 0;
 217    while (s[end] != '\0') {
 218      end++;
 219    }
 220    DOUBLE_NUM_T weight, ten, sum;
 221    set_lw (sum, 0);
 222    set_lw (weight, 1);
 223    set_lw (ten, 10);
 224    for (int k = end - 1; k >= 0; k--) {
 225      DOUBLE_NUM_T term;
 226      int digit = s[k] - '0';
 227      set_lw (term, digit);
 228      term = double_umul (p, M_LONG_INT, term, weight);
 229      sum = double_uadd (p, M_LONG_INT, sum, term);
 230      weight = double_umul (p, M_LONG_INT, weight, ten);
 231    }
 232    if (sign == -1) {
 233      HW (sum) = HW (sum) | D_SIGN;
 234    }
 235    VALUE (z) = sum;
 236    STATUS (z) = INIT_MASK;
 237    return A68G_TRUE;
 238  }
 239  
 240  
 241  //! @brief Transform string into real-16.
 242  
 243  DOUBLE_T string_to_double (NODE_T *p, char *s)
 244  {
 245    errno = 0;
 246    #if defined (PRECISE_CONVERSIONS)
 247      ADDR_T pop_sp = A68G_SP;
 248      int digs = DIGITS (M_LONG_LONG_REAL);
 249      A68G_SP += SIZE_MP (digs); // In case z is TOS.
 250      MP_T *z = nil_mp (p, digs);
 251      string_to_mp (p, z, s, digs);
 252      if (A68G_NAN_MP (z)) {
 253        return nanq ("");
 254      }
 255      DOUBLE_T u = mp_to_double (p, z, digs);
 256      A68G_SP = pop_sp;
 257      CHECK_DOUBLE_REAL (p, u, M_LONG_REAL);
 258      return u;
 259    #else
 260      errno = 0;
 261      DOUBLE_T y[A68G_DOUBLE_DIG];
 262      for (int i = 0; i < A68G_DOUBLE_DIG; i++) {
 263        y[i] = 0.0q;
 264      }
 265      while (IS_SPACE (s[0])) {
 266        s++;
 267      }
 268      // Scan mantissa digits and put them into "y".
 269      DOUBLE_T W;
 270      if (s[0] == '-') {
 271        W = -1.0q;
 272      } else {
 273        W = 1.0q;
 274      }
 275      if (s[0] == '+' || s[0] == '-') {
 276        s++;
 277      }
 278      while (s[0] == '0') {
 279        s++;
 280      }
 281      int dot = -1, pos = 0, pow = 0;
 282      while (pow < A68G_DOUBLE_DIG && s[pos] != NULL_CHAR && (IS_DIGIT (s[pos]) || s[pos] == POINT_CHAR)) {
 283        if (s[pos] == POINT_CHAR) {
 284          dot = pos;
 285        } else {
 286          int val = (int) s[pos] - (int) '0';
 287          y[pow] = W * val;
 288          W /= 10.0q;
 289          pow++;
 290        }
 291        pos++;
 292      }
 293      while (IS_DIGIT (s[pos])) {
 294        pos++;
 295      }
 296      // Sum from low to high to preserve precision.
 297      DOUBLE_T sum = a68g_neumaier_sum_double (y, A68G_DOUBLE_DIG);
 298      // See if there is an exponent.
 299      int expo;
 300      if (s[pos] != NULL_CHAR && TO_UPPER (s[pos]) == TO_UPPER (EXPONENT_CHAR)) {
 301        expo = (int) strtol (&(s[++pos]), NO_REF, 10);
 302      } else {
 303        expo = 0;
 304      }
 305      // Standardise.
 306      if (dot >= 0) {
 307        expo += dot - 1;
 308      } else {
 309        expo += pow - 1;
 310      }
 311      while (sum != 0.0q && fabs_double (sum) < 1.0q) {
 312        sum *= 10.0q;
 313        expo -= 1;
 314      }
 315      if (errno == 0) {
 316        DOUBLE_T w = sum * ten_up_double (expo);
 317        return w;
 318      } else {
 319        return 0.0q;
 320      }
 321    #endif
 322  }
 323  
 324  #endif
 325  
 326  
 327  //! @brief Set "z" to 10 ** "n".
 328  
 329  MP_T *ten_up_mp (NODE_T * p, MP_T * z, int n, int digs)
 330  {
 331    #if (A68G_LEVEL >= 3)
 332      static MP_T y[LOG_MP_RADIX] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
 333    #else
 334      static MP_T y[LOG_MP_RADIX] = { 1, 10, 100, 1000, 10000, 100000, 1000000 };
 335    #endif
 336    if (n >= 0) {
 337      (void) set_mp (z, y[n % LOG_MP_RADIX], n / LOG_MP_RADIX, digs);
 338    } else {
 339      (void) set_mp (z, y[(LOG_MP_RADIX + n % LOG_MP_RADIX) % LOG_MP_RADIX], (n + 1) / LOG_MP_RADIX - 1, digs);
 340    }
 341    check_mp_exp (p, z);
 342    return z;
 343  }
 344  
 345  
 346  //! @brief INT value of BITS denotation
 347  
 348  UNSIGNED_T bits_to_int (NODE_T * p, char *str)
 349  {
 350    errno = 0;
 351    char *radix = NO_TEXT, *end = NO_TEXT;
 352    int base = (int) a68g_strtou (str, &radix, 10);
 353    if (radix != NO_TEXT && TO_UPPER (radix[0]) == TO_UPPER (RADIX_CHAR) && errno == 0) {
 354      if (base < 2 || base > 16) {
 355        diagnostic (A68G_RUNTIME_ERROR, p, ERROR_INVALID_RADIX, base);
 356        exit_genie (p, A68G_RUNTIME_ERROR);
 357      }
 358      UNSIGNED_T bits = a68g_strtou (&(radix[1]), &end, base);
 359      if (end != NO_TEXT && end[0] == NULL_CHAR && errno == 0) {
 360        return bits;
 361      }
 362    }
 363    diagnostic (A68G_RUNTIME_ERROR, p, ERROR_IN_DENOTATION, M_BITS);
 364    exit_genie (p, A68G_RUNTIME_ERROR);
 365    return 0;
 366  }
 367  
 368  
 369  //! @brief Convert to other radix, binary up to hexadecimal.
 370  
 371  BOOL_T convert_radix (NODE_T * p, UNSIGNED_T z, int radix, int width)
 372  {
 373    reset_transput_buffer (EDIT_BUFFER);
 374    if (radix < 2 || radix > 16) {
 375      radix = 16;
 376    }
 377    if (width > 0) {
 378      while (width > 0) {
 379        int digit = (int) (z % (UNSIGNED_T) radix);
 380        plusto_transput_buffer (p, digchar (digit), EDIT_BUFFER);
 381        width--;
 382        z /= (UNSIGNED_T) radix;
 383      }
 384      return z == 0;
 385    } else if (width == 0) {
 386      do {
 387        int digit = (int) (z % (UNSIGNED_T) radix);
 388        plusto_transput_buffer (p, digchar (digit), EDIT_BUFFER);
 389        z /= (UNSIGNED_T) radix;
 390      } while (z > 0);
 391      return A68G_TRUE;
 392    } else {
 393      return A68G_FALSE;
 394    }
 395  }
 396  
 397  #if (A68G_LEVEL >= 3)
 398  
 399  
 400  //! @brief Convert to other radix, binary up to hexadecimal.
 401  
 402  BOOL_T convert_radix_double (NODE_T * p, DOUBLE_NUM_T z, int radix, int width)
 403  {
 404    if (radix < 2 || radix > 16) {
 405      radix = 16;
 406    }
 407    DOUBLE_NUM_T w, rad;
 408    set_lw (rad, radix);
 409    reset_transput_buffer (EDIT_BUFFER);
 410    if (width > 0) {
 411      while (width > 0) {
 412        w = double_udiv (p, M_LONG_INT, z, rad, 1);
 413        plusto_transput_buffer (p, digchar (LW (w)), EDIT_BUFFER);
 414        width--;
 415        z = double_udiv (p, M_LONG_INT, z, rad, 0);
 416      }
 417      return D_ZERO (z);
 418    } else if (width == 0) {
 419      do {
 420        w = double_udiv (p, M_LONG_INT, z, rad, 1);
 421        plusto_transput_buffer (p, digchar (LW (w)), EDIT_BUFFER);
 422        z = double_udiv (p, M_LONG_INT, z, rad, 0);
 423      }
 424      while (!D_ZERO (z));
 425      return A68G_TRUE;
 426    } else {
 427      return A68G_FALSE;
 428    }
 429  }
 430  
 431  #endif
 432  
 433  
 434  //! @brief Convert string to multi-precision number.
 435  
 436  MP_T *string_to_mp (NODE_T * p, MP_T * z, char *s, int digs)
 437  {
 438    ABEND (s == NO_TEXT, ERROR_INTERNAL_CONSISTENCY, NO_TEXT);
 439    BOOL_T ok = A68G_TRUE;
 440    errno = 0;
 441    SET_MP_ZERO (z, digs);
 442    while (IS_SPACE (s[0])) {
 443      s++;
 444    }
 445    // Get the sign.
 446    int sign = (s[0] == '-' ? -1 : 1);
 447    if (s[0] == '+' || s[0] == '-') {
 448      s++;
 449    }
 450    // Scan mantissa digs and put them into "z".
 451    while (s[0] == '0') {
 452      s++;
 453    }
 454    int i = 0, dig = 1;
 455    INT_T sum = 0, dot = -1, one = -1, pow = 0, W = MP_RADIX / 10;
 456    while (s[i] != NULL_CHAR && dig <= digs && (IS_DIGIT (s[i]) || s[i] == POINT_CHAR)) {
 457      if (s[i] == POINT_CHAR) {
 458        dot = i;
 459      } else {
 460        int value = (int) s[i] - (int) '0';
 461        if (one < 0 && value > 0) {
 462          one = pow;
 463        }
 464        sum += W * value;
 465        if (one >= 0) {
 466          W /= 10;
 467        }
 468        pow++;
 469        if (W < 1) {
 470          MP_DIGIT (z, dig++) = (MP_T) sum;
 471          sum = 0;
 472          W = MP_RADIX / 10;
 473        }
 474      }
 475      i++;
 476    }
 477    // Store the last digs.
 478    if (dig <= digs) {
 479      MP_DIGIT (z, dig++) = (MP_T) sum;
 480    }
 481    // See if there is an exponent.
 482    INT_T expo;
 483    if (s[i] != NULL_CHAR && TO_UPPER (s[i]) == TO_UPPER (EXPONENT_CHAR)) {
 484      char *end;
 485      expo = (int) strtol (&(s[++i]), &end, 10);
 486      ok = (BOOL_T) (end[0] == NULL_CHAR);
 487    } else {
 488      expo = 0;
 489      ok = (BOOL_T) (s[i] == NULL_CHAR);
 490    }
 491    // Calculate effective exponent.
 492    if (dot >= 0) {
 493      if (one > dot) {
 494        expo -= one - dot + 1;
 495      } else {
 496        expo += dot - 1;
 497      }
 498    } else {
 499      expo += pow - 1;
 500    }
 501    (void) align_mp (z, &expo, digs);
 502    MP_EXPONENT (z) = (MP_DIGIT (z, 1) == 0 ? 0 : (MP_T) expo);
 503    MP_DIGIT (z, 1) *= sign;
 504    check_mp_exp (p, z);
 505    if (errno == 0 && ok) {
 506      return z;
 507    } else {
 508      set_nan_mp (z);
 509      return z;
 510    }
 511  }
 512  
 513  
 514  //! @brief Convert integer to multi-precison number.
 515  
 516  MP_T *int_to_mp (NODE_T * p, MP_T * z, INT_T k, int digs)
 517  {
 518    int sign_k = 1;
 519    if (k < 0) {
 520      k = -k;
 521      sign_k = -1;
 522    }
 523    INT_T m = k, n = 0;
 524    while ((m /= MP_RADIX) != 0) {
 525      n++;
 526    }
 527    (void) set_mp (z, 0, n, digs);
 528    for (int j = 1 + n; j >= 1; j--) {
 529      MP_DIGIT (z, j) = (MP_T) (k % MP_RADIX);
 530      k /= MP_RADIX;
 531    }
 532    MP_DIGIT (z, 1) = sign_k * MP_DIGIT (z, 1);
 533    check_mp_exp (p, z);
 534    return z;
 535  }
 536  
 537  
 538  //! @brief Convert unt to multi-precison number.
 539  
 540  MP_T *unt_to_mp (NODE_T * p, MP_T * z, UNSIGNED_T k, int digs)
 541  {
 542    int m = k, n = 0;
 543    while ((m /= MP_RADIX) != 0) {
 544      n++;
 545    }
 546    (void) set_mp (z, 0, n, digs);
 547    for (int j = 1 + n; j >= 1; j--) {
 548      MP_DIGIT (z, j) = (MP_T) (k % MP_RADIX);
 549      k /= MP_RADIX;
 550    }
 551    check_mp_exp (p, z);
 552    return z;
 553  }
 554  
 555  
 556  //! @brief Convert multi-precision number to integer.
 557  
 558  INT_T mp_to_int (NODE_T * p, MP_T * z, int digs)
 559  {
 560    // This routines looks a lot like "strtol". 
 561    INT_T expo = (int) MP_EXPONENT (z), sum = 0, weight = 1;
 562    if (expo >= digs) {
 563      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, MOID (p));
 564      exit_genie (p, A68G_RUNTIME_ERROR);
 565    }
 566    BOOL_T negative = (BOOL_T) (MP_DIGIT (z, 1) < 0);
 567    if (negative) {
 568      MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
 569    }
 570    for (int j = 1 + expo; j >= 1; j--) {
 571      if ((MP_INT_T) MP_DIGIT (z, j) > A68G_MAX_INT / weight) {
 572        diagnostic (A68G_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, M_INT);
 573        exit_genie (p, A68G_RUNTIME_ERROR);
 574      }
 575      INT_T term = (MP_INT_T) MP_DIGIT (z, j) * weight;
 576      if (sum > A68G_MAX_INT - term) {
 577        diagnostic (A68G_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, M_INT);
 578        exit_genie (p, A68G_RUNTIME_ERROR);
 579      }
 580      sum += term;
 581      weight *= MP_RADIX;
 582    }
 583    return negative ? -sum : sum;
 584  }
 585  
 586  
 587  //! @brief Convert REAL_T to multi-precison number.
 588  
 589  MP_T *real_to_mp (NODE_T * p, MP_T * z, REAL_T x, int digs)
 590  {
 591    #if (A68G_LEVEL >= 3)
 592      return double_to_mp (p, z, (DOUBLE_T) x, digs);
 593    #else
 594      if (a68g_isnan_real (x)) {
 595        set_nan_mp (z);
 596      } else if (x == A68G_PLUS_INF_REAL) {
 597        set_plus_inf_mp (z);
 598      } else if (x == A68G_MINUS_INF_REAL) {
 599        set_minus_inf_mp (z);
 600      } else {
 601        // Legacy code.
 602        SET_MP_ZERO (z, digs);
 603        if (a68g_isinf_real (x)) {
 604          if (x == A68G_PLUS_INF_REAL) {
 605            set_plus_inf_mp (z);
 606          } else {
 607            set_minus_inf_mp (z);
 608          }
 609          return z;
 610        } else if (a68g_isnan_real (x)) {
 611          set_nan_mp (z);
 612          return z;
 613        }
 614        if (x == 0.0) {
 615          return z;
 616        }
 617        // Small integers can be done better by int_to_mp.
 618        if (ABS (x) < MP_RADIX && trunc (x) == x) {
 619          return int_to_mp (p, z, (INT_T) trunc (x), digs);
 620        }
 621        int sign_x = SIGN (x);
 622        // Scale to [0, 0.1>.
 623        REAL_T a = ABS (x);
 624        INT_T expo = (int) log10 (a);
 625        a /= ten_up (expo);
 626        expo--;
 627        if (a >= 1) {
 628          a /= 10;
 629          expo++;
 630        }
 631        // Transport digs of x to the mantissa of z.
 632        for (int j = 1, k = 0; k <= A68G_REAL_DIG && j <= digs; j++, k += LOG_MP_RADIX) {
 633          DOUBLE_T dig;
 634          a = modf (a * MP_RADIX, &dig);
 635          MP_DIGIT (z, j) = (INT_T) dig;
 636        }
 637        // Return the result.
 638        (void) align_mp (z, &expo, digs);
 639        MP_EXPONENT (z) = (MP_T) expo;
 640        MP_DIGIT (z, 1) *= sign_x;
 641        check_mp_exp (p, z);
 642      }
 643      return z;
 644    #endif
 645  }
 646  
 647  
 648  //! @brief Convert multi-precision number to real.
 649  
 650  REAL_T mp_to_real (NODE_T * p, MP_T * z, int digs)
 651  {
 652    #if (A68G_LEVEL >= 3)
 653      return (REAL_T) mp_to_double (p, z, digs);
 654    #else 
 655      if (A68G_NAN_MP (z)) {
 656        return A68G_NAN_REAL;
 657      } else if (A68G_PLUS_INF_MP (z)) {
 658        return A68G_PLUS_INF_REAL;
 659      } else if (A68G_MINUS_INF_MP (z)) {
 660        return A68G_MINUS_INF_REAL;
 661      } else {
 662        // Legacy code, like "strtod".
 663        (void) p;
 664        if (MP_EXPONENT (z) * (MP_T) LOG_MP_RADIX <= (MP_T) A68G_REAL_MIN_EXP) {
 665          return 0;
 666        } else {
 667          REAL_T terms[MP_MAX_DIGITS];
 668          REAL_T weight = 1.0;
 669          int lim = A68G_MIN (digs, MP_MAX_DIGITS);
 670          for (int k = 0; k < lim; k++) {
 671            terms[k] = ABS (MP_DIGIT (z, k + 1)) * weight;
 672            weight /= MP_RADIX;
 673          }
 674          REAL_T sum = a68g_neumaier_sum_real (terms, lim) * ten_up ((int) (MP_EXPONENT (z) * LOG_MP_RADIX));
 675          CHECK_REAL (p, sum, M_REAL);
 676          return MP_DIGIT (z, 1) >= 0 ? sum : -sum;
 677        }
 678      }
 679    #endif
 680  }
 681  
 682  #if (A68G_LEVEL >= 3)
 683  
 684  
 685  //! @brief Convert double to multi-precison number.
 686  
 687  MP_T *double_to_mp (NODE_T * p, MP_T * z, DOUBLE_T u, int digs)
 688  {
 689    SET_MP_ZERO (z, digs);
 690    // Infinity and beyond.
 691    if (a68g_isinf_double (u)) {
 692      if (u == A68G_PLUS_INF_DOUBLE) {
 693        set_plus_inf_mp (z);
 694      } else {
 695        set_minus_inf_mp (z);
 696      }
 697      return z;
 698    } else if (a68g_isnan_double (u)) {
 699      set_nan_mp (z);
 700      return z;
 701    }
 702    if (u == 0.0q) {
 703      return z;
 704    }
 705    // Small integers can be done better by int_to_mp.
 706    if (ABS (u) < MP_RADIX && trunc_double (u) == u) {
 707      return int_to_mp (p, z, (int) trunc_double (u), digs);
 708    }
 709    // Conversion depends on availability of real*16.
 710    #if defined (PRECISE_CONVERSIONS)
 711      DOUBLE_NUM_T w = dble (u);
 712      if (SUBNORMAL (w)) {
 713        // Strict flush-to-zero policy.
 714        return z;
 715      } 
 716      // The IEEE 128-bit float is dissected as mantissa * 2^exponent.
 717      // Conversion to MP number is done with the decimal LONG LONG library.
 718      // This is sufficiently accurate.
 719      int sign_u = SIGN (u);
 720      // Scale to [0.5, 1>
 721      int exp2;
 722      (void) frexpq (u, &exp2);
 723      // Convert mantissa to MP.
 724      IEEE_754_MANTISSA (w);
 725      double_int_to_mp (p, z, w, digs);
 726      // Compute MP value by multiplying by 2 ^ exponent.
 727      // Scale the 113-bit unsigned mantissa back to [0.5, 1> by
 728      // subtracting 113 from the exponent.
 729      ADDR_T pop_sp = A68G_SP;
 730      A68G_SP += SIZE_MP (digs); // In case z is TOS.
 731      ldexp_mp (p, z, z, exp2 - 113, digs);
 732      A68G_SP = pop_sp;
 733    #else
 734      // Legacy approach.
 735      int sign_u = SIGN (u);
 736      // Scale to [0, 0.1>.
 737      DOUBLE_T a = ABS (u);
 738      INT_T expo = (int) log10_double (a);
 739      a /= ten_up_double (expo);
 740      expo--;
 741      if (a >= 1.0q) {
 742        a /= 10.0q;
 743        expo++;
 744      }
 745      // Transport digits of u to the mantissa of z.
 746      for (int j = 1, k = 0; k <= 1 + A68G_DOUBLE_DIG && j <= digs; j++, k += LOG_MP_RADIX) {
 747        DOUBLE_T dig;
 748        a = modfq (a * MP_RADIX_Q, &dig);
 749        MP_DIGIT (z, j) = (INT_T) dig;
 750      }
 751      (void) align_mp (z, &expo, digs);
 752      MP_EXPONENT (z) = (MP_T) expo;
 753    #endif
 754    // Check and return.
 755    MP_DIGIT (z, 1) *= sign_u;
 756    check_mp_exp (p, z);
 757    return z;
 758  }
 759  
 760  
 761  //! @brief Convert multi-precision number to real.
 762  
 763  DOUBLE_T mp_to_double (NODE_T * p, MP_T * z, int digs)
 764  {
 765    if (A68G_PLUS_INF_MP (z)) {
 766      return A68G_PLUS_INF_DOUBLE;
 767    } else if (A68G_MINUS_INF_MP (z)) {
 768      return A68G_MINUS_INF_DOUBLE;
 769    } else if (A68G_NAN_MP (z)) {
 770      return A68G_NAN_DOUBLE;
 771    }
 772    if (MP_EXPONENT (z) * (MP_T) LOG_MP_RADIX <= (MP_T) A68G_DOUBLE_MIN_EXP) {
 773      return 0.0q;
 774    } else {
 775      // Compute the DOUBLE value of |z|.
 776      int lim = A68G_MIN (digs, MP_MAX_DIGITS);
 777      DOUBLE_T terms[MP_MAX_DIGITS], f = 1.0q;
 778      for (int k = 0; k < lim; k++) {
 779        terms[k] = ABS (MP_DIGIT (z, k + 1)) * f;
 780        f /= MP_RADIX_Q;
 781      }
 782      DOUBLE_T u = a68g_neumaier_sum_double (terms, lim) * ten_up_double ((int) (MP_EXPONENT (z) * LOG_MP_RADIX));
 783      #if defined (PRECISE_CONVERSIONS)
 784        // We have a good approximation of the 128-bit real value, in 'u'.
 785        // Conversion error is expected one ulp, that we correct here.
 786        // This way, double_to_mp and mp_to_double are effectively 'mirrors' and 
 787        // the relation 'x = SHORTEN LENG x' holds for LONG REAL x.
 788        #define H_MASK 0x0000ffffffffffffULL
 789        #define L_MASK 0xffffffffffffffffULL
 790        DOUBLE_NUM_T v = dble (u);
 791        if (SUBNORMAL (v)) {
 792          // Strict flush-to-zero policy.
 793          return 0.0q;
 794        }
 795        // Rounding a converted number of form (2^k - 1 ulp), k<0. 
 796        // Doing this here guarantees correct exponent later.
 797        if ((HW (v) & H_MASK) == H_MASK && (LW (v) == L_MASK)) {
 798          LW (v) = 0;
 799          HW (v)++;
 800        }
 801        // Scale to [0.5, 1>
 802        int exp2;
 803        (void) frexpq (u, &exp2);
 804        // Convert u 113-bit mantissa to MP unsigned integer.
 805        // Keep the exponent.
 806        UNSIGNED_T expo = HW (v) & DOUBLE_EXPONENT_MASK;
 807        IEEE_754_MANTISSA (v);
 808        ADDR_T pop_sp = A68G_SP;
 809        A68G_SP += SIZE_MP (digs); // In case z is TOS.
 810        MP_T *mant = nil_mp (p, digs);
 811        ldexp_mp (p, mant, z, 113 - exp2, digs);
 812        round_mp (p, mant, mant, digs);
 813        DOUBLE_NUM_T w = mp_to_double_int (p, mant, digs);
 814        A68G_SP = pop_sp;
 815        IEEE_754_MANTISSA (w);
 816        // Restore the exponent.
 817        HW (w) = (HW (w) & ~DOUBLE_EXPONENT_MASK) | expo;
 818        u = w.f;
 819        #undef H_MASK
 820        #undef L_MASK
 821      #endif
 822      CHECK_DOUBLE_REAL (p, u, M_LONG_REAL);
 823      return (MP_DIGIT (z, 1) >= 0 ? u : -u);
 824    }
 825  }
 826  
 827  MP_T *double_int_to_mp (NODE_T * p, MP_T * z, DOUBLE_NUM_T k, int digs)
 828  {
 829    int negative = D_NEG (k);
 830    if (negative) {
 831      k = neg_double_int (k);
 832    }
 833    DOUBLE_NUM_T radix;
 834    set_lw (radix, MP_RADIX);
 835    DOUBLE_NUM_T k2 = k;
 836    int n = 0;
 837    do {
 838      k2 = double_udiv (p, M_LONG_INT, k2, radix, 0);
 839      if (!D_ZERO (k2)) {
 840        n++;
 841      }
 842    }
 843    while (!D_ZERO (k2));
 844    SET_MP_ZERO (z, digs);
 845    MP_EXPONENT (z) = (MP_T) n;
 846    for (int j = 1 + n; j >= 1; j--) {
 847      DOUBLE_NUM_T term = double_udiv (p, M_LONG_INT, k, radix, 1);
 848      MP_DIGIT (z, j) = (MP_T) LW (term);
 849      k = double_udiv (p, M_LONG_INT, k, radix, 0);
 850    }
 851    MP_DIGIT (z, 1) = (negative ? -MP_DIGIT (z, 1) : MP_DIGIT (z, 1));
 852    check_mp_exp (p, z);
 853    return z;
 854  }
 855  
 856  
 857  //! @brief Convert multi-precision number to integer.
 858  
 859  DOUBLE_NUM_T mp_to_double_int (NODE_T * p, MP_T * z, int digs)
 860  {
 861    // This routines looks a lot like "strtol". 
 862    int expo = (int) MP_EXPONENT (z);
 863    DOUBLE_NUM_T sum, weight;
 864    set_lw (sum, 0);
 865    set_lw (weight, 1);
 866    BOOL_T negative;
 867    if (expo >= digs) {
 868      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, MOID (p));
 869      exit_genie (p, A68G_RUNTIME_ERROR);
 870    }
 871    negative = (BOOL_T) (MP_DIGIT (z, 1) < 0);
 872    if (negative) {
 873      MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
 874    }
 875    for (int j = 1 + expo; j >= 1; j--) {
 876      DOUBLE_NUM_T term, digit, radix;
 877      set_lw (digit, (MP_INT_T) MP_DIGIT (z, j));
 878      term = double_umul (p, M_LONG_INT, digit, weight);
 879      sum = double_uadd (p, M_LONG_INT, sum, term);
 880      set_lw (radix, MP_RADIX);
 881      weight = double_umul (p, M_LONG_INT, weight, radix);
 882    }
 883    if (negative) {
 884      return neg_double_int (sum);
 885    } else {
 886      return sum;
 887    }
 888  }
 889  
 890  #endif
 891  
 892  
 893  //! @brief Convert string to required mode and store.
 894  
 895  BOOL_T genie_string_to_value_internal (NODE_T * p, MOID_T * m, char *a, BYTE_T * item)
 896  {
 897    errno = 0;
 898    // strto.. does not mind empty strings.
 899    if (strlen (a) == 0) {
 900      return A68G_FALSE;
 901    }
 902    if (m == M_INT) {
 903      A68G_INT *z = (A68G_INT *) item;
 904      char *end;
 905      VALUE (z) = (INT_T) a68g_strtoi (a, &end, 10);
 906      if (end[0] == NULL_CHAR && errno == 0) {
 907        STATUS (z) = INIT_MASK;
 908        return A68G_TRUE;
 909      } else {
 910        return A68G_FALSE;
 911      }
 912    }
 913    if (m == M_REAL) {
 914      A68G_REAL *z = (A68G_REAL *) item;
 915      VALUE (z) = string_to_real (p, a);
 916      if (errno == 0) {
 917        STATUS (z) = INIT_MASK;
 918        return A68G_TRUE;
 919      } else {
 920        return A68G_FALSE;
 921      }
 922    }
 923    #if (A68G_LEVEL >= 3)
 924      if (m == M_LONG_INT) {
 925        A68G_LONG_INT *z = (A68G_LONG_INT *) item;
 926        if (string_to_double_int (p, z, a) == A68G_FALSE) {
 927          return A68G_FALSE;
 928        }
 929        STATUS (z) = INIT_MASK;
 930        return A68G_TRUE;
 931      }
 932      if (m == M_LONG_REAL) {
 933        A68G_LONG_REAL *z = (A68G_LONG_REAL *) item;
 934        VALUE (z).f = string_to_double (p, a);
 935        PRELUDE_ERROR (errno != 0, p, ERROR_MATH, M_LONG_REAL);
 936        if (errno == 0) {
 937          STATUS (z) = INIT_MASK;
 938          return A68G_TRUE;
 939        } else {
 940          return A68G_FALSE;
 941        }
 942      }
 943      if (m == M_LONG_BITS) {
 944        A68G_LONG_BITS *z = (A68G_LONG_BITS *) item;
 945        int ret = A68G_TRUE;
 946        DOUBLE_NUM_T b;
 947        set_lw (b, 0x0);
 948        if (a[0] == FLIP_CHAR || a[0] == FLOP_CHAR) {
 949          // [] BOOL denotation is "TTFFFFTFT ...".
 950          if (strlen (a) > (size_t) A68G_LONG_BITS_WIDTH) {
 951            errno = ERANGE;
 952            ret = A68G_FALSE;
 953          } else {
 954            int n = 1;
 955            UNSIGNED_T k = 0x1;
 956            for (INT_T j = (INT_T) strlen (a) - 1; j >= 0; j--) {
 957              if (a[j] == FLIP_CHAR) {
 958                if (n <= A68G_LONG_BITS_WIDTH / 2) {
 959                  LW (b) |= k;
 960                } else {
 961                  HW (b) |= k;
 962                }
 963              } else if (a[j] != FLOP_CHAR) {
 964                ret = A68G_FALSE;
 965              }
 966              k <<= 1;
 967            }
 968          }
 969          VALUE (z) = b;
 970        } else {
 971          // BITS denotation.
 972          VALUE (z) = string_to_double_bits (p, a);
 973        }
 974        return ret;
 975      }
 976    #else
 977      if (m == M_LONG_BITS || m == M_LONG_LONG_BITS) {
 978        int digits = DIGITS (m);
 979        int status = A68G_TRUE;
 980        ADDR_T pop_sp = A68G_SP;
 981        MP_T *z = (MP_T *) item;
 982        if (a[0] == FLIP_CHAR || a[0] == FLOP_CHAR) {
 983          // [] BOOL denotation is "TTFFFFTFT ...".
 984          if (strlen (a) > (size_t) A68G_BITS_WIDTH) {
 985            errno = ERANGE;
 986            status = A68G_FALSE;
 987          } else {
 988            MP_T *w = lit_mp (p, 1, 0, digits);
 989            SET_MP_ZERO (z, digits);
 990            for (INT_T j = (INT_T) strlen (a) - 1; j >= 0; j--) {
 991              if (a[j] == FLIP_CHAR) {
 992                (void) add_mp (p, z, z, w, digits);
 993              } else if (a[j] != FLOP_CHAR) {
 994                status = A68G_FALSE;
 995              }
 996              (void) mul_mp_digit (p, w, w, (MP_T) 2, digits);
 997            }
 998          }
 999        } else {
1000          // BITS denotation is also allowed.
1001          mp_strtou (p, z, a, m);
1002        }
1003        A68G_SP = pop_sp;
1004        if (errno != 0 || status == A68G_FALSE) {
1005          return A68G_FALSE;
1006        }
1007        SET_INIT_MP (z);
1008        return A68G_TRUE;
1009      }
1010    #endif
1011    if (m == M_LONG_INT || m == M_LONG_LONG_INT) {
1012      int digits = DIGITS (m);
1013      MP_T *z = (MP_T *) item;
1014      string_to_mp (p, z, a, digits);
1015      if (A68G_NAN_MP (z)) {
1016        return A68G_FALSE;
1017      }
1018      if (!check_mp_int (z, m)) {
1019        errno = ERANGE;
1020        return A68G_FALSE;
1021      }
1022      SET_INIT_MP (z);
1023      return A68G_TRUE;
1024    }
1025    if (m == M_LONG_REAL || m == M_LONG_LONG_REAL) {
1026      int digits = DIGITS (m);
1027      MP_T *z = (MP_T *) item;
1028      string_to_mp (p, z, a, digits);
1029      if (A68G_NAN_MP (z)) {
1030        return A68G_FALSE;
1031      }
1032      SET_INIT_MP (z);
1033      return A68G_TRUE;
1034    }
1035    if (m == M_BOOL) {
1036      A68G_BOOL *z = (A68G_BOOL *) item;
1037      char q = a[0], flip = FLIP_CHAR, flop = FLOP_CHAR;
1038      if (q == flip || q == flop) {
1039        VALUE (z) = (BOOL_T) (q == flip);
1040        STATUS (z) = INIT_MASK;
1041        return A68G_TRUE;
1042      } else {
1043        return A68G_FALSE;
1044      }
1045    }
1046    if (m == M_BITS) {
1047      A68G_BITS *z = (A68G_BITS *) item;
1048      int status = A68G_TRUE;
1049      if (a[0] == FLIP_CHAR || a[0] == FLOP_CHAR) {
1050        // [] BOOL denotation is "TTFFFFTFT ...".
1051        if (strlen (a) > (size_t) A68G_BITS_WIDTH) {
1052          errno = ERANGE;
1053          status = A68G_FALSE;
1054        } else {
1055          UNSIGNED_T k = 0x1;
1056          VALUE (z) = 0;
1057          for (INT_T j = (INT_T) strlen (a) - 1; j >= 0; j--) {
1058            if (a[j] == FLIP_CHAR) {
1059              VALUE (z) += k;
1060            } else if (a[j] != FLOP_CHAR) {
1061              status = A68G_FALSE;
1062            }
1063            k <<= 1;
1064          }
1065        }
1066      } else {
1067        // BITS denotation is also allowed.
1068        VALUE (z) = bits_to_int (p, a);
1069      }
1070      if (errno != 0 || status == A68G_FALSE) {
1071        return A68G_FALSE;
1072      }
1073      STATUS (z) = INIT_MASK;
1074      return A68G_TRUE;
1075    }
1076    return A68G_FALSE;
1077  }
     

This website is archived by the National Library of the Netherlands.

© J.M. van der Veer   •   jmvdveer@algol68genie.nl