📄 float.c
字号:
/*------------------------------------------------------------------------- * * float.c * Functions for the built-in floating-point types. * * Portions Copyright (c) 1996-2008, 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.153 2008/01/01 19:45:52 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <ctype.h>#include <float.h>#include <math.h>#include <limits.h>#include "catalog/pg_type.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/* Visual C++ etc lacks NAN, and won't accept 0.0/0.0. NAN definition from * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrfNotNumberNANItems.asp */#if defined(WIN32) && !defined(NAN)static const uint32 nan[2] = {0xffffffff, 0x7fffffff};#define NAN (*(const double *) nan)#endif/* not sure what the following should be, but better to make it over-sufficient */#define MAXFLOATWIDTH 64#define MAXDOUBLEWIDTH 128/* * check to see if a float4/8 val has underflowed or overflowed */#define CHECKFLOATVAL(val, inf_is_valid, zero_is_valid) \do { \ if (isinf(val) && !(inf_is_valid)) \ ereport(ERROR, \ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \ errmsg("value out of range: overflow"))); \ \ if ((val) == 0.0 && !(zero_is_valid)) \ ereport(ERROR, \ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \ errmsg("value out of range: underflow"))); \} while(0)/* ========== USER I/O ROUTINES ========== *//* Configurable GUC parameter */int extra_float_digits = 0; /* Added to DBL_DIG or FLT_DIG */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; else if (val > 0) return 1; else return -1;}/* * 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 */#ifdef HAVE_BUGGY_IRIX_STRTOD /* * In some IRIX versions, strtod() recognizes only "inf", so if the input * is "infinity" we have to skip over "inity". Also, it may return * positive infinity for "-inf". */ if (isinf(val)) { 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 (pg_strncasecmp(num, "-inf", 4) == 0) { val = -get_float4_infinity(); endptr = num + 4; } }#endif /* HAVE_BUGGY_IRIX_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 */ CHECKFLOATVAL((float4) val, isinf(val), val == 0); 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 */#ifdef HAVE_BUGGY_IRIX_STRTOD /* * In some IRIX versions, strtod() recognizes only "inf", so if the input * is "infinity" we have to skip over "inity". Also, it may return * positive infinity for "-inf". */ if (isinf(val)) { 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 (pg_strncasecmp(num, "-inf", 4) == 0) { val = -get_float8_infinity(); endptr = num + 4; } }#endif /* HAVE_BUGGY_IRIX_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))); CHECKFLOATVAL(val, true, true); 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) */Datumfloat4abs(PG_FUNCTION_ARGS){ float4 arg1 = PG_GETARG_FLOAT4(0); PG_RETURN_FLOAT4((float4) fabs(arg1));}/* * float4um - returns -arg1 (unary minus) */Datumfloat4um(PG_FUNCTION_ARGS){ float4 arg1 = PG_GETARG_FLOAT4(0); float4 result; result = ((arg1 != 0) ? -(arg1) : arg1); CHECKFLOATVAL(result, isinf(arg1), true); PG_RETURN_FLOAT4(result);}Datumfloat4up(PG_FUNCTION_ARGS){ float4 arg = PG_GETARG_FLOAT4(0); PG_RETURN_FLOAT4(arg);}Datumfloat4larger(PG_FUNCTION_ARGS){ float4 arg1 = PG_GETARG_FLOAT4(0); float4 arg2 = PG_GETARG_FLOAT4(1); float4 result; if (float4_cmp_internal(arg1, arg2) > 0) result = arg1; else result = arg2; PG_RETURN_FLOAT4(result);}Datumfloat4smaller(PG_FUNCTION_ARGS){ float4 arg1 = PG_GETARG_FLOAT4(0); float4 arg2 = PG_GETARG_FLOAT4(1); float4 result; if (float4_cmp_internal(arg1, arg2) < 0) result = arg1; else result = arg2; PG_RETURN_FLOAT4(result);}/* * ====================== * FLOAT8 BASE OPERATIONS * ====================== *//* * float8abs - returns |arg1| (absolute value) */Datumfloat8abs(PG_FUNCTION_ARGS){ float8 arg1 = PG_GETARG_FLOAT8(0); PG_RETURN_FLOAT8(fabs(arg1));}/* * float8um - returns -arg1 (unary minus) */Datumfloat8um(PG_FUNCTION_ARGS){ float8 arg1 = PG_GETARG_FLOAT8(0); float8 result; result = ((arg1 != 0) ? -(arg1) : arg1); CHECKFLOATVAL(result, isinf(arg1), true); PG_RETURN_FLOAT8(result);}Datumfloat8up(PG_FUNCTION_ARGS){ float8 arg = PG_GETARG_FLOAT8(0); PG_RETURN_FLOAT8(arg);}Datumfloat8larger(PG_FUNCTION_ARGS){ float8 arg1 = PG_GETARG_FLOAT8(0); float8 arg2 = PG_GETARG_FLOAT8(1); float8 result; if (float8_cmp_internal(arg1, arg2) > 0) result = arg1; else result = arg2; PG_RETURN_FLOAT8(result);}Datumfloat8smaller(PG_FUNCTION_ARGS){ float8 arg1 = PG_GETARG_FLOAT8(0); float8 arg2 = PG_GETARG_FLOAT8(1); float8 result; if (float8_cmp_internal(arg1, arg2) < 0) result = arg1; else result = arg2; PG_RETURN_FLOAT8(result);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -