postgresql/src/backend/utils/adt/numutils.c

407 lines
8.3 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* numutils.c--
* utility functions for I/O of built-in numeric types.
*
* integer: itoa, ltoa
* floating point: ftoa, atof1
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/numutils.c,v 1.11 1997/08/12 20:16:02 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <stdio.h> /* for sprintf() */
#include <errno.h>
#include <math.h>
#include "postgres.h"
#include "utils/builtins.h" /* where the declarations go */
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#else
# include <string.h>
#endif
#include <port-protos.h> /* ecvt(), fcvt() */
int32
pg_atoi(char *s, int size, int c)
{
long l;
char *badp = (char *) NULL;
Assert(s);
errno = 0;
l = strtol(s, &badp, 10);
if (errno) /* strtol must set ERANGE */
elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
if (badp && *badp && (*badp != c))
elog(WARN, "pg_atoi: error in \"%s\": can\'t parse \"%s\"", s, badp);
switch (size) {
case sizeof(int32):
#ifdef HAS_LONG_LONG
/* won't get ERANGE on these with 64-bit longs... */
if (l < -0x80000000L) {
errno = ERANGE;
elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
}
if (l > 0x7fffffffL) {
errno = ERANGE;
elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
}
#endif /* HAS_LONG_LONG */
break;
case sizeof(int16):
if (l < -0x8000) {
errno = ERANGE;
elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
}
if (l > 0x7fff) {
errno = ERANGE;
elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
}
break;
case sizeof(int8):
if (l < -0x80) {
errno = ERANGE;
elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
}
if (l > 0x7f) {
errno = ERANGE;
elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
}
break;
default:
elog(WARN, "pg_atoi: invalid result size: %d", size);
}
return((int32) l);
}
/*
* itoa - converts a short int to its string represention
*
* Note:
* previously based on ~ingres/source/gutil/atoi.c
* now uses vendor's sprintf conversion
*/
void
itoa(int i, char *a)
{
sprintf(a, "%hd", (short)i);
}
/*
* ltoa - converts a long int to its string represention
*
* Note:
* previously based on ~ingres/source/gutil/atoi.c
* now uses vendor's sprintf conversion
*/
void
ltoa(int32 l, char *a)
{
sprintf(a, "%d", l);
}
/*
** ftoa - FLOATING POINT TO ASCII CONVERSION
**
** CODE derived from ingres, ~ingres/source/gutil/ftoa.c
**
** 'Value' is converted to an ascii character string and stored
** into 'ascii'. Ascii should have room for at least 'width' + 1
** characters. 'Width' is the width of the output field (max).
** 'Prec' is the number of characters to put after the decimal
** point. The format of the output string is controlled by
** 'format'.
**
** 'Format' can be:
** e or E: "E" format output
** f or F: "F" format output
** g or G: "F" format output if it will fit, otherwise
** use "E" format.
** n or N: same as G, but decimal points will not always
** be aligned.
**
** If 'format' is upper case, the "E" comes out in upper case;
** otherwise it comes out in lower case.
**
** When the field width is not big enough, it fills the field with
** stars ("*****") and returns zero. Normal return is the width
** of the output field (sometimes shorter than 'width').
*/
int
ftoa(double value, char *ascii, int width, int prec1, char format)
{
#ifndef HAVE_FCVT
char out[256];
char fmt[256];
int ret;
(void) sprintf(fmt, "%%%d.%d%c", width, prec1, format);
(void) sprintf(out, fmt, value);
if ((ret = strlen(out)) > width) {
memset(ascii, '*', width - 2);
ascii[width] = 0;
return(0);
}
strcpy(ascii, out);
return(ret);
#else
auto int expon;
auto int sign;
register int avail = 0;
register char *a = NULL;
register char *p = NULL;
char mode;
int lowercase;
int prec;
/* extern char *ecvt(), *fcvt();*/
prec = prec1;
mode = format;
lowercase = 'a' - 'A';
if (mode >= 'a')
mode -= 'a' - 'A';
else
lowercase = 0;
if (mode != 'E') {
/* try 'F' style output */
p = fcvt(value, prec, &expon, &sign);
avail = width;
a = ascii;
/* output sign */
if (sign) {
avail--;
*a++ = '-';
}
/* output '0' before the decimal point */
if (expon <= 0) {
*a++ = '0';
avail--;
}
/* compute space length left after dec pt and fraction */
avail -= prec + 1;
if (mode == 'G')
avail -= 4;
if (avail >= expon) {
/* it fits. output */
while (expon > 0) {
/* output left of dp */
expon--;
if (*p) {
*a++ = *p++;
} else
*a++ = '0';
}
/* output fraction (right of dec pt) */
avail = expon;
goto frac_out;
}
/* won't fit; let's hope for G format */
}
if (mode != 'F') {
/* try to do E style output */
p = ecvt(value, prec + 1, &expon, &sign);
avail = width - 5;
a = ascii;
/* output the sign */
if (sign) {
*a++ = '-';
avail--;
}
}
/* check for field too small */
if (mode == 'F' || avail < prec) {
/* sorry joker, you lose */
a = ascii;
for (avail = width; avail > 0; avail--)
*a++ = '*';
*a = 0;
return (0);
}
/* it fits; output the number */
mode = 'E';
/* output the LHS single digit */
*a++ = *p++;
expon--;
/* output the rhs */
avail = 1;
frac_out:
*a++ = '.';
while (prec > 0) {
prec--;
if (avail < 0) {
avail++;
*a++ = '0';
} else {
if (*p)
*a++ = *p++;
else
*a++ = '0';
}
}
/* output the exponent */
if (mode == 'E') {
*a++ = 'E' + lowercase;
if (expon < 0) {
*a++ = '-';
expon = -expon;
} else
*a++ = '+';
*a++ = (expon / 10) % 10 + '0';
*a++ = expon % 10 + '0';
}
/* output spaces on the end in G format */
if (mode == 'G') {
*a++ = ' ';
*a++ = ' ';
*a++ = ' ';
*a++ = ' ';
}
/* finally, we can return */
*a = 0;
avail = a - ascii;
return (avail);
1996-10-31 11:23:28 +01:00
#endif /* !BSD44_derived */
}
/*
** atof1 - ASCII TO FLOATING CONVERSION
**
** CODE derived from ~ingres/source/gutil/atof.c
**
** Converts the string 'str' to floating point and stores the
** result into the cell pointed to by 'val'.
**
** The syntax which it accepts is pretty much what you would
** expect. Basically, it is:
** {<sp>} [+|-] {<sp>} {<digit>} [.{digit}] {<sp>} [<exp>]
** where <exp> is "e" or "E" followed by an integer, <sp> is a
** space character, <digit> is zero through nine, [] is zero or
** one, and {} is zero or more.
**
** Parameters:
** str -- string to convert.
** val -- pointer to place to put the result (which
** must be type double).
**
** Returns:
** zero -- ok.
** -1 -- syntax error.
** +1 -- overflow (not implemented).
**
** Side Effects:
** clobbers *val.
*/
int
atof1(char *str, double *val)
{
register char *p;
double v;
double fact;
int minus;
register char c;
int expon;
register int gotmant;
v = 0.0;
p = str;
minus = 0;
/* skip leading blanks */
while ((c = *p) != '\0') {
if (c != ' ')
break;
p++;
}
/* handle possible sign */
switch (c) {
case '-':
minus++;
case '+':
p++;
}
/* skip blanks after sign */
while ((c = *p) != '\0') {
if (c != ' ')
break;
p++;
}
/* start collecting the number to the decimal point */
gotmant = 0;
for (;;) {
c = *p;
if (c < '0' || c > '9')
break;
v = v * 10.0 + (c - '0');
gotmant++;
p++;
}
/* check for fractional part */
if (c == '.') {
fact = 1.0;
for (;;) {
c = *++p;
if (c < '0' || c > '9')
break;
fact *= 0.1;
v += (c - '0') * fact;
gotmant++;
}
}
/* skip blanks before possible exponent */
while ((c = *p) != '\0') {
if (c != ' ')
break;
p++;
}
/* test for exponent */
if (c == 'e' || c == 'E') {
p++;
expon = pg_atoi(p, sizeof(expon), '\0');
if (!gotmant)
v = 1.0;
fact = expon;
v *= pow(10.0, fact);
} else {
/* if no exponent, then nothing */
if (c != 0)
return (-1);
}
/* store the result and exit */
if (minus)
v = -v;
*val = v;
return (0);
}