diff options
Diffstat (limited to 'builtin.c')
-rw-r--r-- | builtin.c | 326 |
1 files changed, 253 insertions, 73 deletions
@@ -3,7 +3,7 @@ */ /* - * Copyright (C) 1986, 1988, 1989, 1991-2011 the Free Software Foundation, Inc. + * Copyright (C) 1986, 1988, 1989, 1991-2012 the Free Software Foundation, Inc. * * This file is part of GAWK, the GNU implementation of the * AWK Programming Language. @@ -135,7 +135,7 @@ do_exp(int nargs) tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("exp: received non-numeric argument")); - d = force_number(tmp); + d = force_number(tmp)->numbr; DEREF(tmp); errno = 0; res = exp(d); @@ -459,7 +459,7 @@ do_int(int nargs) tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("int: received non-numeric argument")); - d = force_number(tmp); + d = force_number(tmp)->numbr; d = double_to_int(d); DEREF(tmp); return make_number((AWKNUM) d); @@ -537,7 +537,7 @@ do_log(int nargs) tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("log: received non-numeric argument")); - arg = (double) force_number(tmp); + arg = force_number(tmp)->numbr; if (arg < 0.0) warning(_("log: received negative argument %g"), arg); d = log(arg); @@ -546,6 +546,42 @@ do_log(int nargs) } +#ifdef HAVE_MPFR + +/* + * mpz2mpfr --- convert an arbitrary-precision integer to a float + * without any loss of precision. The returned value is only + * good for temporary use. + */ + + +static mpfr_ptr +mpz2mpfr(mpz_ptr zi) +{ + size_t prec; + static mpfr_t mpfrval; + static int inited = FALSE; + int tval; + + /* estimate minimum precision for exact conversion */ + prec = mpz_sizeinbase(zi, 2); /* most significant 1 bit position starting at 1 */ + prec -= (size_t) mpz_scan1(zi, 0); /* least significant 1 bit index starting at 0 */ + if (prec < MPFR_PREC_MIN) + prec = MPFR_PREC_MIN; + else if (prec > MPFR_PREC_MAX) + prec = MPFR_PREC_MAX; + + if (! inited) { + mpfr_init2(mpfrval, prec); + inited = TRUE; + } else + mpfr_set_prec(mpfrval, prec); + tval = mpfr_set_z(mpfrval, zi, ROUND_MODE); + IEEE_FMT(mpfrval, tval); + return mpfrval; +} +#endif + /* * format_tree() formats arguments of sprintf, * and accordingly to a fmt_string providing a format like in @@ -603,7 +639,7 @@ format_tree( size_t cur_arg = 0; NODE *r = NULL; - int i; + int i, nc; int toofew = FALSE; char *obuf, *obufout; size_t osiz, ofre; @@ -617,7 +653,7 @@ format_tree( long *cur = NULL; uintmax_t uval; int sgn; - int base = 0; + int base; /* * Although this is an array, the elements serve two different * purposes. The first element is the general buffer meant @@ -635,7 +671,7 @@ format_tree( char *cend = &cpbufs[0].stackbuf[sizeof(cpbufs[0].stackbuf)]; char *cp; const char *fill; - AWKNUM tmpval; + AWKNUM tmpval = 0.0; char signchar = FALSE; size_t len; int zero_flag = FALSE; @@ -643,6 +679,12 @@ format_tree( int ii, jj; char *chp; size_t copy_count, char_count; +#ifdef HAVE_MPFR + mpz_ptr zi; + mpfr_ptr mf; +#endif + enum { MP_INT_WITH_PREC = 1, MP_INT_WITHOUT_PREC, MP_FLOAT } fmt_type; + static const char sp[] = " "; static const char zero_string[] = "0"; static const char lchbuf[] = "0123456789abcdef"; @@ -728,11 +770,19 @@ format_tree( cur = &fw; fw = 0; prec = 0; + base = 0; argnum = 0; + base = 0; have_prec = FALSE; signchar = FALSE; zero_flag = FALSE; quote_flag = FALSE; +#ifdef HAVE_MPFR + mf = NULL; + zi = NULL; +#endif + fmt_type = 0; + lj = alt = big_flag = bigbig_flag = small_flag = FALSE; fill = sp; cp = cend; @@ -864,7 +914,8 @@ check_pos: } else { parse_next_arg(); } - *cur = force_number(arg); + (void) force_number(arg); + *cur = get_number_si(arg); if (*cur < 0 && cur == &fw) { *cur = -*cur; lj++; @@ -972,7 +1023,7 @@ check_pos: if ((arg->flags & (MAYBE_NUM|NUMBER)) == MAYBE_NUM) (void) force_number(arg); if (arg->flags & NUMBER) { - uval = (uintmax_t) arg->numbr; + uval = get_number_uj(arg); #if MBS_SUPPORT if (gawk_mb_cur_max > 1) { char buf[100]; @@ -1055,7 +1106,16 @@ out2: case 'i': need_format = FALSE; parse_next_arg(); - tmpval = force_number(arg); + (void) force_number(arg); +#ifdef HAVE_MPFR + if (is_mpg_float(arg)) + goto mpf0; + else if (is_mpg_integer(arg)) + goto mpz0; + else +#endif + tmpval = arg->numbr; + /* * Check for Nan or Inf. */ @@ -1166,7 +1226,78 @@ out2: base += 8; need_format = FALSE; parse_next_arg(); - tmpval = force_number(arg); + (void) force_number(arg); +#ifdef HAVE_MPFR + if (is_mpg_integer(arg)) { +mpz0: + zi = arg->mpg_i; + + if (cs1 != 'd' && cs1 != 'i') { + if (mpz_sgn(zi) <= 0) { + /* + * Negative value or 0 requires special handling. + * Unlike MPFR, GMP does not allow conversion + * to (u)intmax_t. So we first convert GMP type to + * a MPFR type. + */ + mf = mpz2mpfr(zi); + goto mpf1; + } + signchar = FALSE; /* Don't print '+' */ + } + + /* See comments above about when to fill with zeros */ + zero_flag = (! lj + && ((zero_flag && ! have_prec) + || (fw == 0 && have_prec))); + + fmt_type = have_prec ? MP_INT_WITH_PREC : MP_INT_WITHOUT_PREC; + goto fmt0; + + } else if (is_mpg_float(arg)) { +mpf0: + mf = arg->mpg_numbr; + if (! mpfr_number_p(mf)) { + /* inf or NaN */ + cs1 = 'g'; + fmt_type = MP_FLOAT; + goto fmt1; + } + + if (cs1 != 'd' && cs1 != 'i') { +mpf1: + /* + * The output of printf("%#.0x", 0) is 0 instead of 0x, hence <= in + * the comparison below. + */ + if (mpfr_sgn(mf) <= 0) { + if (! mpfr_fits_intmax_p(mf, ROUND_MODE)) { + /* -ve number is too large */ + cs1 = 'g'; + fmt_type = MP_FLOAT; + goto fmt1; + } + + tmpval = uval = (uintmax_t) mpfr_get_sj(mf, ROUND_MODE); + if (! alt && have_prec && prec == 0 && tmpval == 0) + goto pr_tail; /* printf("%.0x", 0) is no characters */ + goto int0; + } + signchar = FALSE; /* Don't print '+' */ + } + + /* See comments above about when to fill with zeros */ + zero_flag = (! lj + && ((zero_flag && ! have_prec) + || (fw == 0 && have_prec))); + + (void) mpfr_get_z(mpzval, mf, MPFR_RNDZ); /* convert to GMP integer */ + fmt_type = have_prec ? MP_INT_WITH_PREC : MP_INT_WITHOUT_PREC; + zi = mpzval; + goto fmt0; + } else +#endif + tmpval = arg->numbr; /* * ``The result of converting a zero value with a @@ -1185,14 +1316,16 @@ out2: if (tmpval < 0) { uval = (uintmax_t) (intmax_t) tmpval; - if ((AWKNUM)(intmax_t)uval != - double_to_int(tmpval)) + if ((AWKNUM)(intmax_t)uval != double_to_int(tmpval)) goto out_of_range; } else { uval = (uintmax_t) tmpval; if ((AWKNUM)uval != double_to_int(tmpval)) goto out_of_range; } +#ifdef HAVE_MPFR + int0: +#endif /* * When to fill with zeroes is of course not simple. * First: No zero fill if left-justifying. @@ -1275,7 +1408,7 @@ out2: lintwarn(_("[s]printf: value %g is out of range for `%%%c' format"), (double) tmpval, cs1); cs1 = 'g'; - goto format_float; + goto fmt1; case 'F': #if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1 @@ -1289,11 +1422,28 @@ out2: case 'E': need_format = FALSE; parse_next_arg(); - tmpval = force_number(arg); - format_float: + (void) force_number(arg); + + if (! is_mpg_number(arg)) + tmpval = arg->numbr; +#ifdef HAVE_MPFR + else if (is_mpg_float(arg)) { + mf = arg->mpg_numbr; + fmt_type = MP_FLOAT; + } else { + /* arbitrary-precision integer, convert to MPFR float */ + assert(mf == NULL); + mf = mpz2mpfr(arg->mpg_i); + fmt_type = MP_FLOAT; + } +#endif + fmt1: if (! have_prec) prec = DEFAULT_G_PRECISION; - chksize(fw + prec + 9); /* 9 == slop */ +#ifdef HAVE_MPFR + fmt0: +#endif + chksize(fw + prec + 11); /* 11 == slop */ cp = cpbuf; *cp++ = '%'; if (lj) @@ -1306,25 +1456,46 @@ out2: *cp++ = '0'; if (quote_flag) *cp++ = '\''; - strcpy(cp, "*.*"); - cp += 3; - *cp++ = cs1; - *cp = '\0'; + #if defined(LC_NUMERIC) if (quote_flag && ! use_lc_numeric) setlocale(LC_NUMERIC, ""); #endif - { - int n; - while ((n = snprintf(obufout, ofre, cpbuf, - (int) fw, (int) prec, - (double) tmpval)) >= ofre) - chksize(n) + + switch (fmt_type) { +#ifdef HAVE_MPFR + case MP_INT_WITH_PREC: + sprintf(cp, "*.*Z%c", cs1); + while ((nc = mpfr_snprintf(obufout, ofre, cpbuf, + (int) fw, (int) prec, zi)) >= ofre) + chksize(nc) + break; + case MP_INT_WITHOUT_PREC: + sprintf(cp, "*Z%c", cs1); + while ((nc = mpfr_snprintf(obufout, ofre, cpbuf, + (int) fw, zi)) >= ofre) + chksize(nc) + break; + case MP_FLOAT: + sprintf(cp, "*.*R*%c", cs1); + while ((nc = mpfr_snprintf(obufout, ofre, cpbuf, + (int) fw, (int) prec, ROUND_MODE, mf)) >= ofre) + chksize(nc) + break; +#endif + default: + sprintf(cp, "*.*%c", cs1); + while ((nc = snprintf(obufout, ofre, cpbuf, + (int) fw, (int) prec, + (double) tmpval)) >= ofre) + chksize(nc) } + #if defined(LC_NUMERIC) if (quote_flag && ! use_lc_numeric) setlocale(LC_NUMERIC, "C"); #endif + len = strlen(obufout); ofre -= len; obufout += len; @@ -1365,6 +1536,7 @@ out: if (obuf != NULL) efree(obuf); } + if (r == NULL) gawk_exit(EXIT_FATAL); return r; @@ -1476,7 +1648,7 @@ do_sqrt(int nargs) tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("sqrt: received non-numeric argument")); - arg = (double) force_number(tmp); + arg = (double) force_number(tmp)->numbr; DEREF(tmp); if (arg < 0.0) warning(_("sqrt: called with negative argument %g"), arg); @@ -1495,9 +1667,16 @@ do_substr(int nargs) double d_index = 0, d_length = 0; size_t src_len; - if (nargs == 3) - POP_NUMBER(d_length); - POP_NUMBER(d_index); + if (nargs == 3) { + t1 = POP_NUMBER(); + d_length = get_number_d(t1); + DEREF(t1); + } + + t1 = POP_NUMBER(); + d_index = get_number_d(t1); + DEREF(t1); + t1 = POP_STRING(); if (nargs == 3) { @@ -1681,7 +1860,8 @@ do_strftime(int nargs) t2 = POP_SCALAR(); if (do_lint && (t2->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("strftime: received non-numeric second argument")); - clock_val = (long) force_number(t2); + (void) force_number(t2); + clock_val = get_number_si(t2); if (clock_val < 0) fatal(_("strftime: second argument less than 0 or too big for time_t")); fclock = (time_t) clock_val; @@ -2099,8 +2279,8 @@ do_atan2(int nargs) if ((t2->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("atan2: received non-numeric second argument")); } - d1 = force_number(t1); - d2 = force_number(t2); + d1 = force_number(t1)->numbr; + d2 = force_number(t2)->numbr; DEREF(t1); DEREF(t2); return make_number((AWKNUM) atan2(d1, d2)); @@ -2117,7 +2297,7 @@ do_sin(int nargs) tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("sin: received non-numeric argument")); - d = sin((double) force_number(tmp)); + d = sin((double) force_number(tmp)->numbr); DEREF(tmp); return make_number((AWKNUM) d); } @@ -2133,7 +2313,7 @@ do_cos(int nargs) tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("cos: received non-numeric argument")); - d = cos((double) force_number(tmp)); + d = cos((double) force_number(tmp)->numbr); DEREF(tmp); return make_number((AWKNUM) d); } @@ -2186,7 +2366,7 @@ do_srand(int nargs) tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("srand: received non-numeric argument")); - srandom((unsigned int) (save_seed = (long) force_number(tmp))); + srandom((unsigned int) (save_seed = (long) force_number(tmp)->numbr)); DEREF(tmp); } return make_number((AWKNUM) ret); @@ -2463,15 +2643,16 @@ do_sub(int nargs, unsigned int flags) if (t1->stlen > 0 && (t1->stptr[0] == 'g' || t1->stptr[0] == 'G')) how_many = -1; else { - d = force_number(t1); - + (void) force_number(t1); + d = get_number_d(t1); if ((t1->flags & NUMCUR) != 0) goto set_how_many; how_many = 1; } } else { - d = force_number(t1); + (void) force_number(t1); + d = get_number_d(t1); set_how_many: if (d < 1) how_many = 1; @@ -2777,15 +2958,15 @@ do_lshift(int nargs) if ((s2->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("lshift: received non-numeric second argument")); } - val = force_number(s1); - shift = force_number(s2); + val = force_number(s1)->numbr; + shift = force_number(s2)->numbr; if (do_lint) { if (val < 0 || shift < 0) - lintwarn(_("lshift(%lf, %lf): negative values will give strange results"), val, shift); + lintwarn(_("lshift(%f, %f): negative values will give strange results"), val, shift); if (double_to_int(val) != val || double_to_int(shift) != shift) - lintwarn(_("lshift(%lf, %lf): fractional values will be truncated"), val, shift); + lintwarn(_("lshift(%f, %f): fractional values will be truncated"), val, shift); if (shift >= sizeof(uintmax_t) * CHAR_BIT) - lintwarn(_("lshift(%lf, %lf): too large shift value will give strange results"), val, shift); + lintwarn(_("lshift(%f, %f): too large shift value will give strange results"), val, shift); } DEREF(s1); @@ -2814,15 +2995,15 @@ do_rshift(int nargs) if ((s2->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("rshift: received non-numeric second argument")); } - val = force_number(s1); - shift = force_number(s2); + val = force_number(s1)->numbr; + shift = force_number(s2)->numbr; if (do_lint) { if (val < 0 || shift < 0) - lintwarn(_("rshift(%lf, %lf): negative values will give strange results"), val, shift); + lintwarn(_("rshift(%f, %f): negative values will give strange results"), val, shift); if (double_to_int(val) != val || double_to_int(shift) != shift) - lintwarn(_("rshift(%lf, %lf): fractional values will be truncated"), val, shift); + lintwarn(_("rshift(%f, %f): fractional values will be truncated"), val, shift); if (shift >= sizeof(uintmax_t) * CHAR_BIT) - lintwarn(_("rshift(%lf, %lf): too large shift value will give strange results"), val, shift); + lintwarn(_("rshift(%f, %f): too large shift value will give strange results"), val, shift); } DEREF(s1); @@ -2851,13 +3032,13 @@ do_and(int nargs) if ((s2->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("and: received non-numeric second argument")); } - left = force_number(s1); - right = force_number(s2); + left = force_number(s1)->numbr; + right = force_number(s2)->numbr; if (do_lint) { if (left < 0 || right < 0) - lintwarn(_("and(%lf, %lf): negative values will give strange results"), left, right); + lintwarn(_("and(%f, %f): negative values will give strange results"), left, right); if (double_to_int(left) != left || double_to_int(right) != right) - lintwarn(_("and(%lf, %lf): fractional values will be truncated"), left, right); + lintwarn(_("and(%f, %f): fractional values will be truncated"), left, right); } DEREF(s1); @@ -2886,13 +3067,13 @@ do_or(int nargs) if ((s2->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("or: received non-numeric second argument")); } - left = force_number(s1); - right = force_number(s2); + left = force_number(s1)->numbr; + right = force_number(s2)->numbr; if (do_lint) { if (left < 0 || right < 0) - lintwarn(_("or(%lf, %lf): negative values will give strange results"), left, right); + lintwarn(_("or(%f, %f): negative values will give strange results"), left, right); if (double_to_int(left) != left || double_to_int(right) != right) - lintwarn(_("or(%lf, %lf): fractional values will be truncated"), left, right); + lintwarn(_("or(%f, %f): fractional values will be truncated"), left, right); } DEREF(s1); @@ -2915,8 +3096,6 @@ do_xor(int nargs) AWKNUM left, right; POP_TWO_SCALARS(s1, s2); - left = force_number(s1); - right = force_number(s2); if (do_lint) { if ((s1->flags & (NUMCUR|NUMBER)) == 0) @@ -2924,13 +3103,13 @@ do_xor(int nargs) if ((s2->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("xor: received non-numeric second argument")); } - left = force_number(s1); - right = force_number(s2); + left = force_number(s1)->numbr; + right = force_number(s2)->numbr; if (do_lint) { if (left < 0 || right < 0) - lintwarn(_("xor(%lf, %lf): negative values will give strange results"), left, right); + lintwarn(_("xor(%f, %f): negative values will give strange results"), left, right); if (double_to_int(left) != left || double_to_int(right) != right) - lintwarn(_("xor(%lf, %lf): fractional values will be truncated"), left, right); + lintwarn(_("xor(%f, %f): fractional values will be truncated"), left, right); } DEREF(s1); @@ -2955,16 +3134,14 @@ do_compl(int nargs) tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("compl: received non-numeric argument")); - d = force_number(tmp); + d = force_number(tmp)->numbr; DEREF(tmp); if (do_lint) { - if ((tmp->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("compl: received non-numeric argument")); if (d < 0) - lintwarn(_("compl(%lf): negative value will give strange results"), d); + lintwarn(_("compl(%f): negative value will give strange results"), d); if (double_to_int(d) != d) - lintwarn(_("compl(%lf): fractional value will be truncated"), d); + lintwarn(_("compl(%f): fractional value will be truncated"), d); } uval = (uintmax_t) d; @@ -2982,11 +3159,11 @@ do_strtonum(int nargs) tmp = POP_SCALAR(); if ((tmp->flags & (NUMBER|NUMCUR)) != 0) - d = (AWKNUM) force_number(tmp); - else if (isnondecimal(tmp->stptr, use_lc_numeric)) + d = (AWKNUM) force_number(tmp)->numbr; + else if (get_numbase(tmp->stptr, use_lc_numeric) != 10) d = nondec2awknum(tmp->stptr, tmp->stlen); else - d = (AWKNUM) force_number(tmp); + d = (AWKNUM) force_number(tmp)->numbr; DEREF(tmp); return make_number((AWKNUM) d); @@ -3236,7 +3413,10 @@ do_dcngettext(int nargs) } #endif - POP_NUMBER(d); /* third argument */ + t2 = POP_NUMBER(); /* third argument */ + d = get_number_d(t2); + DEREF(t2); + number = (unsigned long) double_to_int(d); t2 = POP_STRING(); /* second argument */ string2 = t2->stptr; |