📄 float.c
字号:
/*------------------------------------------------------------------------- * * float.c * Functions for the built-in floating-point types. * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.115.2.1 2006/04/24 20:36:41 tgl Exp $ * *------------------------------------------------------------------------- *//*---------- * OLD COMMENTS * Basic float4 ops: * float4in, float4out, float4recv, float4send * float4abs, float4um, float4up * Basic float8 ops: * float8in, float8out, float8recv, float8send * float8abs, float8um, float8up * Arithmetic operators: * float4pl, float4mi, float4mul, float4div * float8pl, float8mi, float8mul, float8div * Comparison operators: * float4eq, float4ne, float4lt, float4le, float4gt, float4ge, float4cmp * float8eq, float8ne, float8lt, float8le, float8gt, float8ge, float8cmp * Conversion routines: * ftod, dtof, i4tod, dtoi4, i2tod, dtoi2, itof, ftoi, i2tof, ftoi2 * * Random float8 ops: * dround, dtrunc, dsqrt, dcbrt, dpow, dexp, dlog1 * Arithmetic operators: * float48pl, float48mi, float48mul, float48div * float84pl, float84mi, float84mul, float84div * Comparison operators: * float48eq, float48ne, float48lt, float48le, float48gt, float48ge * float84eq, float84ne, float84lt, float84le, float84gt, float84ge * * (You can do the arithmetic and comparison stuff using conversion * routines, but then you pay the overhead of invoking a separate * conversion function...) * * XXX GLUESOME STUFF. FIX IT! -AY '94 * * Added some additional conversion routines and cleaned up * a bit of the existing code. Need to change the error checking * for calls to pow(), exp() since on some machines (my Linux box * included) these routines do not set errno. - tgl 97/05/10 *---------- */#include "postgres.h"#include <ctype.h>#include <errno.h>#include <float.h>#include <math.h>#include <limits.h>/* for finite() on Solaris */#ifdef HAVE_IEEEFP_H#include <ieeefp.h>#endif#include "catalog/pg_type.h"#include "fmgr.h"#include "libpq/pqformat.h"#include "utils/array.h"#include "utils/builtins.h"#ifndef M_PI/* from my RH5.2 gcc math.h file - thomas 2000-04-03 */#define M_PI 3.14159265358979323846#endif#ifndef SHRT_MAX#define SHRT_MAX 32767#endif#ifndef SHRT_MIN#define SHRT_MIN (-32768)#endif/* Recent HPUXen have isfinite() macro in place of more standard finite() */#if !defined(HAVE_FINITE) && defined(isfinite)#define finite(x) isfinite(x)#define HAVE_FINITE 1#endif/* not sure what the following should be, but better to make it over-sufficient */#define MAXFLOATWIDTH 64#define MAXDOUBLEWIDTH 128/* ========== USER I/O ROUTINES ========== */#define FLOAT4_MAX FLT_MAX#define FLOAT4_MIN FLT_MIN#define FLOAT8_MAX DBL_MAX#define FLOAT8_MIN DBL_MIN/* Configurable GUC parameter */int extra_float_digits = 0; /* Added to DBL_DIG or FLT_DIG */static void CheckFloat4Val(double val);static void CheckFloat8Val(double val);static int float4_cmp_internal(float4 a, float4 b);static int float8_cmp_internal(float8 a, float8 b);#ifndef HAVE_CBRTstatic double cbrt(double x);#endif /* HAVE_CBRT *//* * Routines to provide reasonably platform-independent handling of * infinity and NaN. We assume that isinf() and isnan() are available * and work per spec. (On some platforms, we have to supply our own; * see src/port.) However, generating an Infinity or NaN in the first * place is less well standardized; pre-C99 systems tend not to have C99's * INFINITY and NAN macros. We centralize our workarounds for this here. */doubleget_float8_infinity(void){#ifdef INFINITY /* C99 standard way */ return (double) INFINITY;#else /* * On some platforms, HUGE_VAL is an infinity, elsewhere it's just the * largest normal double. We assume forcing an overflow will get us a * true infinity. */ return (double) (HUGE_VAL * HUGE_VAL);#endif}floatget_float4_infinity(void){#ifdef INFINITY /* C99 standard way */ return (float) INFINITY;#else /* * On some platforms, HUGE_VAL is an infinity, elsewhere it's just the * largest normal double. We assume forcing an overflow will get us a * true infinity. */ return (float) (HUGE_VAL * HUGE_VAL);#endif}doubleget_float8_nan(void){#ifdef NAN /* C99 standard way */ return (double) NAN;#else /* Assume we can get a NAN via zero divide */ return (double) (0.0 / 0.0);#endif}floatget_float4_nan(void){#ifdef NAN /* C99 standard way */ return (float) NAN;#else /* Assume we can get a NAN via zero divide */ return (float) (0.0 / 0.0);#endif}/* * Returns -1 if 'val' represents negative infinity, 1 if 'val' * represents (positive) infinity, and 0 otherwise. On some platforms, * this is equivalent to the isinf() macro, but not everywhere: C99 * does not specify that isinf() needs to distinguish between positive * and negative infinity. */intis_infinite(double val){ int inf = isinf(val); if (inf == 0) return 0; if (val > 0) return 1; return -1;}/* * check to see if a float4 val is outside of the FLOAT4_MIN, * FLOAT4_MAX bounds. * * raise an ereport() error if it is */static voidCheckFloat4Val(double val){ if (fabs(val) > FLOAT4_MAX) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("type \"real\" value out of range: overflow"))); if (val != 0.0 && fabs(val) < FLOAT4_MIN) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("type \"real\" value out of range: underflow")));}/* * check to see if a float8 val is outside of the FLOAT8_MIN, * FLOAT8_MAX bounds. * * raise an ereport() error if it is */static voidCheckFloat8Val(double val){ if (fabs(val) > FLOAT8_MAX) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("type \"double precision\" value out of range: overflow"))); if (val != 0.0 && fabs(val) < FLOAT8_MIN) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("type \"double precision\" value out of range: underflow")));}/* * float4in - converts "num" to float * restricted syntax: * {<sp>} [+|-] {digit} [.{digit}] [<exp>] * where <sp> is a space, digit is 0-9, * <exp> is "e" or "E" followed by an integer. */Datumfloat4in(PG_FUNCTION_ARGS){ char *num = PG_GETARG_CSTRING(0); char *orig_num; double val; char *endptr; /* * endptr points to the first character _after_ the sequence we recognized * as a valid floating point number. orig_num points to the original input * string. */ orig_num = num; /* * Check for an empty-string input to begin with, to avoid the vagaries of * strtod() on different platforms. */ if (*num == '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type real: \"%s\"", orig_num))); /* skip leading whitespace */ while (*num != '\0' && isspace((unsigned char) *num)) num++; errno = 0; val = strtod(num, &endptr); /* did we not see anything that looks like a double? */ if (endptr == num || errno != 0) { /* * C99 requires that strtod() accept NaN and [-]Infinity, but not all * platforms support that yet (and some accept them but set ERANGE * anyway...) Therefore, we check for these inputs ourselves. */ if (pg_strncasecmp(num, "NaN", 3) == 0) { val = get_float4_nan(); endptr = num + 3; } else if (pg_strncasecmp(num, "Infinity", 8) == 0) { val = get_float4_infinity(); endptr = num + 8; } else if (pg_strncasecmp(num, "-Infinity", 9) == 0) { val = -get_float4_infinity(); endptr = num + 9; } else if (errno == ERANGE) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("\"%s\" is out of range for type real", orig_num))); else ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type real: \"%s\"", orig_num))); }#ifdef HAVE_BUGGY_SOLARIS_STRTOD else { /* * Many versions of Solaris have a bug wherein strtod sets endptr to * point one byte beyond the end of the string when given "inf" or * "infinity". */ if (endptr != num && endptr[-1] == '\0') endptr--; }#endif /* HAVE_BUGGY_SOLARIS_STRTOD */ /* skip trailing whitespace */ while (*endptr != '\0' && isspace((unsigned char) *endptr)) endptr++; /* if there is any junk left at the end of the string, bail out */ if (*endptr != '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type real: \"%s\"", orig_num))); /* * if we get here, we have a legal double, still need to check to see if * it's a legal float4 */ if (!isinf(val)) CheckFloat4Val(val); PG_RETURN_FLOAT4((float4) val);}/* * float4out - converts a float4 number to a string * using a standard output format */Datumfloat4out(PG_FUNCTION_ARGS){ float4 num = PG_GETARG_FLOAT4(0); char *ascii = (char *) palloc(MAXFLOATWIDTH + 1); if (isnan(num)) PG_RETURN_CSTRING(strcpy(ascii, "NaN")); switch (is_infinite(num)) { case 1: strcpy(ascii, "Infinity"); break; case -1: strcpy(ascii, "-Infinity"); break; default: { int ndig = FLT_DIG + extra_float_digits; if (ndig < 1) ndig = 1; sprintf(ascii, "%.*g", ndig, num); } } PG_RETURN_CSTRING(ascii);}/* * float4recv - converts external binary format to float4 */Datumfloat4recv(PG_FUNCTION_ARGS){ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); PG_RETURN_FLOAT4(pq_getmsgfloat4(buf));}/* * float4send - converts float4 to binary format */Datumfloat4send(PG_FUNCTION_ARGS){ float4 num = PG_GETARG_FLOAT4(0); StringInfoData buf; pq_begintypsend(&buf); pq_sendfloat4(&buf, num); PG_RETURN_BYTEA_P(pq_endtypsend(&buf));}/* * float8in - converts "num" to float8 * restricted syntax: * {<sp>} [+|-] {digit} [.{digit}] [<exp>] * where <sp> is a space, digit is 0-9, * <exp> is "e" or "E" followed by an integer. */Datumfloat8in(PG_FUNCTION_ARGS){ char *num = PG_GETARG_CSTRING(0); char *orig_num; double val; char *endptr; /* * endptr points to the first character _after_ the sequence we recognized * as a valid floating point number. orig_num points to the original input * string. */ orig_num = num; /* * Check for an empty-string input to begin with, to avoid the vagaries of * strtod() on different platforms. */ if (*num == '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type double precision: \"%s\"", orig_num))); /* skip leading whitespace */ while (*num != '\0' && isspace((unsigned char) *num)) num++; errno = 0; val = strtod(num, &endptr); /* did we not see anything that looks like a double? */ if (endptr == num || errno != 0) { /* * C99 requires that strtod() accept NaN and [-]Infinity, but not all * platforms support that yet (and some accept them but set ERANGE * anyway...) Therefore, we check for these inputs ourselves. */ if (pg_strncasecmp(num, "NaN", 3) == 0) { val = get_float8_nan(); endptr = num + 3; } else if (pg_strncasecmp(num, "Infinity", 8) == 0) { val = get_float8_infinity(); endptr = num + 8; } else if (pg_strncasecmp(num, "-Infinity", 9) == 0) { val = -get_float8_infinity(); endptr = num + 9; } else if (errno == ERANGE) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("\"%s\" is out of range for type double precision", orig_num))); else ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type double precision: \"%s\"", orig_num))); }#ifdef HAVE_BUGGY_SOLARIS_STRTOD else { /* * Many versions of Solaris have a bug wherein strtod sets endptr to * point one byte beyond the end of the string when given "inf" or * "infinity". */ if (endptr != num && endptr[-1] == '\0') endptr--; }#endif /* HAVE_BUGGY_SOLARIS_STRTOD */ /* skip trailing whitespace */ while (*endptr != '\0' && isspace((unsigned char) *endptr)) endptr++; /* if there is any junk left at the end of the string, bail out */ if (*endptr != '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type double precision: \"%s\"", orig_num))); if (!isinf(val)) CheckFloat8Val(val); PG_RETURN_FLOAT8(val);}/* * float8out - converts float8 number to a string * using a standard output format */Datumfloat8out(PG_FUNCTION_ARGS){ float8 num = PG_GETARG_FLOAT8(0); char *ascii = (char *) palloc(MAXDOUBLEWIDTH + 1); if (isnan(num)) PG_RETURN_CSTRING(strcpy(ascii, "NaN")); switch (is_infinite(num)) { case 1: strcpy(ascii, "Infinity"); break; case -1: strcpy(ascii, "-Infinity"); break; default: { int ndig = DBL_DIG + extra_float_digits; if (ndig < 1) ndig = 1; sprintf(ascii, "%.*g", ndig, num); } } PG_RETURN_CSTRING(ascii);}/* * float8recv - converts external binary format to float8 */Datumfloat8recv(PG_FUNCTION_ARGS){ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); PG_RETURN_FLOAT8(pq_getmsgfloat8(buf));}/* * float8send - converts float8 to binary format */Datumfloat8send(PG_FUNCTION_ARGS){ float8 num = PG_GETARG_FLOAT8(0); StringInfoData buf; pq_begintypsend(&buf); pq_sendfloat8(&buf, num); PG_RETURN_BYTEA_P(pq_endtypsend(&buf));}/* ========== PUBLIC ROUTINES ========== *//* * ====================== * FLOAT4 BASE OPERATIONS * ====================== *//* * float4abs - returns |arg1| (absolute value) */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -