/* skip leading whitespace */ while (*num != '\0' && isspace((unsignedchar) *num))
num++;
/* *Checkforanempty-stringinputtobeginwith,toavoidthevagariesof *strtod()ondifferentplatforms.
*/ if (*num == '\0')
ereturn(escontext, 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
type_name, orig_string)));
errno = 0;
val = strtof(num, &endptr);
/* did we not see anything that looks like a double? */ if (endptr == num || errno != 0)
{ int save_errno = errno;
/* *C99requiresthatstrtof()acceptNaN,[+-]Infinity,and[+-]Inf, *butnotallplatformssupportallofthese(andsomeacceptthem *butsetERANGEanyway...)Therefore,wecheckfortheseinputs *ourselvesifstrtof()fails. * *Note:C99alsorequireshexadecimalinputaswellassomeextended *formsofNaN,butweconsidertheseformsunportableanddon'ttry *tosupportthem.Youcanuse'emifyourstrtof()takes'em.
*/ if (pg_strncasecmp(num, "NaN", 3) == 0)
{
val = get_float4_nan();
endptr = num + 3;
} elseif (pg_strncasecmp(num, "Infinity", 8) == 0)
{
val = get_float4_infinity();
endptr = num + 8;
} elseif (pg_strncasecmp(num, "+Infinity", 9) == 0)
{
val = get_float4_infinity();
endptr = num + 9;
} elseif (pg_strncasecmp(num, "-Infinity", 9) == 0)
{
val = -get_float4_infinity();
endptr = num + 9;
} elseif (pg_strncasecmp(num, "inf", 3) == 0)
{
val = get_float4_infinity();
endptr = num + 3;
} elseif (pg_strncasecmp(num, "+inf", 4) == 0)
{
val = get_float4_infinity();
endptr = num + 4;
} elseif (pg_strncasecmp(num, "-inf", 4) == 0)
{
val = -get_float4_infinity();
endptr = num + 4;
} elseif (save_errno == ERANGE)
{ /* *SomeplatformsreturnERANGEfordenormalizednumbers(those *thatarenotzero,butaretooclosetozerotohavefull *precision).We'dprefernottothrowerrorforthat,sotryto *detectwhetherit'sa"real"out-of-rangeconditionbychecking *toseeiftheresultiszeroorhuge.
*/ if (val == 0.0 || #if !defined(HUGE_VALF)
isinf(val) #else
(val >= HUGE_VALF || val <= -HUGE_VALF) #endif
)
{ /* see comments in float8in_internal for rationale */ char *errnumber = pstrdup(num);
errnumber[endptr - num] = '\0';
ereturn(escontext, 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("\"%s\" is out of range for type real",
errnumber)));
}
} else
ereturn(escontext, 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
type_name, orig_string)));
}
/* report stopping point if wanted, else complain if not end of string */ if (endptr_p)
*endptr_p = endptr; elseif (*endptr != '\0')
ereturn(escontext, 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
type_name, orig_string)));
return val;
}
/* *float4out-convertsafloat4numbertoastring *usingastandardoutputformat
*/
Datum
float4out(PG_FUNCTION_ARGS)
{
float4 num = PG_GETARG_FLOAT4(0); char *ascii = (char *) palloc(32); int ndig = FLT_DIG + extra_float_digits;
if (extra_float_digits > 0)
{
float_to_shortest_decimal_buf(num, ascii);
PG_RETURN_CSTRING(ascii);
}
/* skip leading whitespace */ while (*num != '\0' && isspace((unsignedchar) *num))
num++;
/* *Checkforanempty-stringinputtobeginwith,toavoidthevagariesof *strtod()ondifferentplatforms.
*/ if (*num == '\0')
ereturn(escontext, 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
type_name, orig_string)));
errno = 0;
val = strtod(num, &endptr);
/* did we not see anything that looks like a double? */ if (endptr == num || errno != 0)
{ int save_errno = errno;
/* *C99requiresthatstrtod()acceptNaN,[+-]Infinity,and[+-]Inf, *butnotallplatformssupportallofthese(andsomeacceptthem *butsetERANGEanyway...)Therefore,wecheckfortheseinputs *ourselvesifstrtod()fails. * *Note:C99alsorequireshexadecimalinputaswellassomeextended *formsofNaN,butweconsidertheseformsunportableanddon'ttry *tosupportthem.Youcanuse'emifyourstrtod()takes'em.
*/ if (pg_strncasecmp(num, "NaN", 3) == 0)
{
val = get_float8_nan();
endptr = num + 3;
} elseif (pg_strncasecmp(num, "Infinity", 8) == 0)
{
val = get_float8_infinity();
endptr = num + 8;
} elseif (pg_strncasecmp(num, "+Infinity", 9) == 0)
{
val = get_float8_infinity();
endptr = num + 9;
} elseif (pg_strncasecmp(num, "-Infinity", 9) == 0)
{
val = -get_float8_infinity();
endptr = num + 9;
} elseif (pg_strncasecmp(num, "inf", 3) == 0)
{
val = get_float8_infinity();
endptr = num + 3;
} elseif (pg_strncasecmp(num, "+inf", 4) == 0)
{
val = get_float8_infinity();
endptr = num + 4;
} elseif (pg_strncasecmp(num, "-inf", 4) == 0)
{
val = -get_float8_infinity();
endptr = num + 4;
} elseif (save_errno == ERANGE)
{ /* *SomeplatformsreturnERANGEfordenormalizednumbers(those *thatarenotzero,butaretooclosetozerotohavefull *precision).We'dprefernottothrowerrorforthat,sotryto *detectwhetherit'sa"real"out-of-rangeconditionbychecking *toseeiftheresultiszeroorhuge. * *Onerror,weintentionallycomplainaboutdoubleprecisionnot *thegiventypename,andweprintonlythepartofthestring *thatisthecurrentnumber.
*/ if (val == 0.0 || val >= HUGE_VAL || val <= -HUGE_VAL)
{ char *errnumber = pstrdup(num);
errnumber[endptr - num] = '\0';
ereturn(escontext, 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("\"%s\" is out of range for type double precision",
errnumber)));
}
} else
ereturn(escontext, 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
type_name, orig_string)));
}
/* report stopping point if wanted, else complain if not end of string */ if (endptr_p)
*endptr_p = endptr; elseif (*endptr != '\0')
ereturn(escontext, 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
type_name, orig_string)));
return val;
}
/* *float8out-convertsfloat8numbertoastring *usingastandardoutputformat
*/
Datum
float8out(PG_FUNCTION_ARGS)
{
float8 num = PG_GETARG_FLOAT8(0);
/* *float4{eq,ne,lt,le,gt,ge}-float4/float4comparisonoperations
*/ int
float4_cmp_internal(float4 a, float4 b)
{ if (float4_gt(a, b)) return1; if (float4_lt(a, b)) return -1; return0;
}
/* *float8{eq,ne,lt,le,gt,ge}-float8/float8comparisonoperations
*/ int
float8_cmp_internal(float8 a, float8 b)
{ if (float8_gt(a, b)) return1; if (float8_lt(a, b)) return -1; return0;
}
/* widen float4 to float8 and then compare */
PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
}
/* *in_rangesupportfunctionforfloat8. * *Note:weneedn'tsupplyafloat8_float4variant,asimplicitcoercion *oftheoffsetvaluetakescareofthatscenariojustaswell.
*/
Datum
in_range_float8_float8(PG_FUNCTION_ARGS)
{
float8 val = PG_GETARG_FLOAT8(0);
float8 base = PG_GETARG_FLOAT8(1);
float8 offset = PG_GETARG_FLOAT8(2); bool sub = PG_GETARG_BOOL(3); bool less = PG_GETARG_BOOL(4);
float8 sum;
/* *RejectnegativeorNaNoffset.Negativeisperspec,andNaNis *becauseappropriatesemanticsforthatseemnon-obvious.
*/ if (isnan(offset) || offset < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
errmsg("invalid preceding or following size in window function")));
/* *Dealwithcaseswherevaland/orbaseisNaN,followingtherulethat *NaNsortsafternon-NaN(cffloat8_cmp_internal).Theoffsetcannot *affecttheconclusion.
*/ if (isnan(val))
{ if (isnan(base))
PG_RETURN_BOOL(true); /* NAN = NAN */ else
PG_RETURN_BOOL(!less); /* NAN > non-NAN */
} elseif (isnan(base))
{
PG_RETURN_BOOL(less); /* non-NAN < NAN */
}
/* *Dealwithcaseswherebothbaseandoffsetareinfinite,andcomputing *base+/-offsetwouldproduceNaN.Thiscorrespondstoawindowframe *whoseboundaryinfinitelyprecedes+inforinfinitelyfollows-inf, *whichisnotwell-defined.Forconsistencywithothercasesinvolving *infinities,suchasthefactthat+infinfinitelyfollows+inf,we *choosetoassumethat+infinfinitelyprecedes+infand-infinfinitely *follows-inf,andthereforethatallfiniteandinfinitevaluesarein *suchawindowframe. * *offsetisknownpositive,soweneedonlycheckthesignofbasein *thistest.
*/ if (isinf(offset) && isinf(base) &&
(sub ? base > 0 : base < 0))
PG_RETURN_BOOL(true);
/* *Otherwiseitshouldbesafetocomputebase+/-offset.Wetrustthe *FPUtocopeifaninputis+/-inforthetruesumwouldoverflow,and *produceasuitablysignedinfinity,whichwillcompareproperlyagainst *valwhetherornotthat'sinfinity.
*/ if (sub)
sum = base - offset; else
sum = base + offset;
if (less)
PG_RETURN_BOOL(val <= sum); else
PG_RETURN_BOOL(val >= sum);
}
/* *in_rangesupportfunctionforfloat4. * *Wewouldneedafloat4_float8variantinanycase,sowesupplythatand *letimplicitcoerciontakecareofthefloat4_float4case.
*/
Datum
in_range_float4_float8(PG_FUNCTION_ARGS)
{
float4 val = PG_GETARG_FLOAT4(0);
float4 base = PG_GETARG_FLOAT4(1);
float8 offset = PG_GETARG_FLOAT8(2); bool sub = PG_GETARG_BOOL(3); bool less = PG_GETARG_BOOL(4);
float8 sum;
/* *RejectnegativeorNaNoffset.Negativeisperspec,andNaNis *becauseappropriatesemanticsforthatseemnon-obvious.
*/ if (isnan(offset) || offset < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
errmsg("invalid preceding or following size in window function")));
/* *Dealwithcaseswherevaland/orbaseisNaN,followingtherulethat *NaNsortsafternon-NaN(cffloat8_cmp_internal).Theoffsetcannot *affecttheconclusion.
*/ if (isnan(val))
{ if (isnan(base))
PG_RETURN_BOOL(true); /* NAN = NAN */ else
PG_RETURN_BOOL(!less); /* NAN > non-NAN */
} elseif (isnan(base))
{
PG_RETURN_BOOL(less); /* non-NAN < NAN */
}
/* *Dealwithcaseswherebothbaseandoffsetareinfinite,andcomputing *base+/-offsetwouldproduceNaN.Thiscorrespondstoawindowframe *whoseboundaryinfinitelyprecedes+inforinfinitelyfollows-inf, *whichisnotwell-defined.Forconsistencywithothercasesinvolving *infinities,suchasthefactthat+infinfinitelyfollows+inf,we *choosetoassumethat+infinfinitelyprecedes+infand-infinfinitely *follows-inf,andthereforethatallfiniteandinfinitevaluesarein *suchawindowframe. * *offsetisknownpositive,soweneedonlycheckthesignofbasein *thistest.
*/ if (isinf(offset) && isinf(base) &&
(sub ? base > 0 : base < 0))
PG_RETURN_BOOL(true);
/* *Otherwiseitshouldbesafetocomputebase+/-offset.Wetrustthe *FPUtocopeifaninputis+/-inforthetruesumwouldoverflow,and *produceasuitablysignedinfinity,whichwillcompareproperlyagainst *valwhetherornotthat'sinfinity.
*/ if (sub)
sum = base - offset; else
sum = base + offset;
if (less)
PG_RETURN_BOOL(val <= sum); else
PG_RETURN_BOOL(val >= sum);
}
/* *ftod-convertsafloat4numbertoafloat8number
*/
Datum
ftod(PG_FUNCTION_ARGS)
{
float4 num = PG_GETARG_FLOAT4(0);
PG_RETURN_FLOAT8((float8) num);
}
/* *dtof-convertsafloat8numbertoafloat4number
*/
Datum
dtof(PG_FUNCTION_ARGS)
{
float8 num = PG_GETARG_FLOAT8(0);
float4 result;
result = (float4) num; if (unlikely(isinf(result)) && !isinf(num))
float_overflow_error(); if (unlikely(result == 0.0f) && num != 0.0)
float_underflow_error();
PG_RETURN_FLOAT4(result);
}
/* *dtoi4-convertsafloat8numbertoanint4number
*/
Datum
dtoi4(PG_FUNCTION_ARGS)
{
float8 num = PG_GETARG_FLOAT8(0);
/* *Getridofanyfractionalpartintheinput.Thisissowedon'tfail *onjust-out-of-rangevaluesthatwouldroundintorange.Note *assumptionthatrint()willpassthroughaNaNorInfunchanged.
*/
num = rint(num);
/* Range check */ if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT32(num)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
PG_RETURN_INT32((int32) num);
}
/* *dtoi2-convertsafloat8numbertoanint2number
*/
Datum
dtoi2(PG_FUNCTION_ARGS)
{
float8 num = PG_GETARG_FLOAT8(0);
/* *Getridofanyfractionalpartintheinput.Thisissowedon'tfail *onjust-out-of-rangevaluesthatwouldroundintorange.Note *assumptionthatrint()willpassthroughaNaNorInfunchanged.
*/
num = rint(num);
/* Range check */ if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT16(num)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
PG_RETURN_INT16((int16) num);
}
/* *i4tod-convertsanint4numbertoafloat8number
*/
Datum
i4tod(PG_FUNCTION_ARGS)
{
int32 num = PG_GETARG_INT32(0);
PG_RETURN_FLOAT8((float8) num);
}
/* *i2tod-convertsanint2numbertoafloat8number
*/
Datum
i2tod(PG_FUNCTION_ARGS)
{
int16 num = PG_GETARG_INT16(0);
PG_RETURN_FLOAT8((float8) num);
}
/* *ftoi4-convertsafloat4numbertoanint4number
*/
Datum
ftoi4(PG_FUNCTION_ARGS)
{
float4 num = PG_GETARG_FLOAT4(0);
/* *Getridofanyfractionalpartintheinput.Thisissowedon'tfail *onjust-out-of-rangevaluesthatwouldroundintorange.Note *assumptionthatrint()willpassthroughaNaNorInfunchanged.
*/
num = rint(num);
/* Range check */ if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT32(num)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
PG_RETURN_INT32((int32) num);
}
/* *ftoi2-convertsafloat4numbertoanint2number
*/
Datum
ftoi2(PG_FUNCTION_ARGS)
{
float4 num = PG_GETARG_FLOAT4(0);
/* *Getridofanyfractionalpartintheinput.Thisissowedon'tfail *onjust-out-of-rangevaluesthatwouldroundintorange.Note *assumptionthatrint()willpassthroughaNaNorInfunchanged.
*/
num = rint(num);
/* Range check */ if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT16(num)))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
PG_RETURN_INT16((int16) num);
}
/* *i4tof-convertsanint4numbertoafloat4number
*/
Datum
i4tof(PG_FUNCTION_ARGS)
{
int32 num = PG_GETARG_INT32(0);
PG_RETURN_FLOAT4((float4) num);
}
/* *i2tof-convertsanint2numbertoafloat4number
*/
Datum
i2tof(PG_FUNCTION_ARGS)
{
int16 num = PG_GETARG_INT16(0);
/* *ThePOSIXspecsaysthatNaN^0=1,and1^NaN=1,whileallother *caseswithNaNinputsyieldNaN(withnoerror).Manyolderplatforms *getoneormoreofthesecaseswrong,sodealwiththemviaexplicit *logicratherthantrustingpow(3).
*/ if (isnan(arg1))
{ if (isnan(arg2) || arg2 != 0.0)
PG_RETURN_FLOAT8(get_float8_nan());
PG_RETURN_FLOAT8(1.0);
} if (isnan(arg2))
{ if (arg1 != 1.0)
PG_RETURN_FLOAT8(get_float8_nan());
PG_RETURN_FLOAT8(1.0);
}
/* *TheSQLspecrequiresthatweemitaparticularSQLSTATEerrorcodefor *certainerrorconditions.Specifically,wedon'treturna *divide-by-zeroerrorcodefor0^-1.
*/ if (arg1 == 0 && arg2 < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
errmsg("zero raised to a negative power is undefined"))); if (arg1 < 0 && floor(arg2) != arg2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
errmsg("a negative number raised to a non-integer power yields a complex result")));
/* *EmitparticularSQLSTATEerrorcodesforln().Thisisrequiredbythe *SQLstandard.
*/ if (arg1 == 0.0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
errmsg("cannot take logarithm of zero"))); if (arg1 < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
errmsg("cannot take logarithm of a negative number")));
result = log(arg1); if (unlikely(isinf(result)) && !isinf(arg1))
float_overflow_error(); if (unlikely(result == 0.0) && arg1 != 1.0)
float_underflow_error();
/* *EmitparticularSQLSTATEerrorcodesforlog().TheSQLspecdoesn't *definelog(),butitdoesdefineln(),soitmakessensetoemitthe *sameerrorcodeforananalogouserrorcondition.
*/ if (arg1 == 0.0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
errmsg("cannot take logarithm of zero"))); if (arg1 < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
errmsg("cannot take logarithm of a negative number")));
result = log10(arg1); if (unlikely(isinf(result)) && !isinf(arg1))
float_overflow_error(); if (unlikely(result == 0.0) && arg1 != 1.0)
float_underflow_error();
/* Per the POSIX spec, return NaN if the input is NaN */ if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
/* *Theprincipalbranchoftheinversecosinefunctionmapsvaluesinthe *range[-1,1]tovaluesintherange[0,Pi],soweshouldrejectany *inputsoutsidethatrangeandtheresultwillalwaysbefinite.
*/ if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
result = acos(arg1); if (unlikely(isinf(result)))
float_overflow_error();
/* Per the POSIX spec, return NaN if the input is NaN */ if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
/* *Theprincipalbranchoftheinversesinefunctionmapsvaluesinthe *range[-1,1]tovaluesintherange[-Pi/2,Pi/2],soweshouldreject *anyinputsoutsidethatrangeandtheresultwillalwaysbefinite.
*/ if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
result = asin(arg1); if (unlikely(isinf(result)))
float_overflow_error();
/* Per the POSIX spec, return NaN if the input is NaN */ if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
/* Be sure to throw an error if the input is infinite --- see dcos() */
errno = 0;
result = tan(arg1); if (errno != 0 || isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
result = 1.0 / result; /* Not checking for overflow because cot(0) == Inf */
/* Per the POSIX spec, return NaN if the input is NaN */ if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
/* Be sure to throw an error if the input is infinite --- see dcos() */
errno = 0;
result = sin(arg1); if (errno != 0 || isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range"))); if (unlikely(isinf(result)))
float_overflow_error();
/* Per the POSIX spec, return NaN if the input is NaN */ if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
/* Be sure to throw an error if the input is infinite --- see dcos() */
errno = 0;
result = tan(arg1); if (errno != 0 || isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range"))); /* Not checking for overflow because tan(pi/2) == Inf */
/* Per the POSIX spec, return NaN if the input is NaN */ if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
INIT_DEGREE_CONSTANTS();
/* *Theprincipalbranchoftheinversecosinefunctionmapsvaluesinthe *range[-1,1]tovaluesintherange[0,180],soweshouldrejectany *inputsoutsidethatrangeandtheresultwillalwaysbefinite.
*/ if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
if (arg1 >= 0.0)
result = acosd_q1(arg1); else
result = 90.0 + asind_q1(-arg1);
if (unlikely(isinf(result)))
float_overflow_error();
/* Per the POSIX spec, return NaN if the input is NaN */ if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
INIT_DEGREE_CONSTANTS();
/* *Theprincipalbranchoftheinversesinefunctionmapsvaluesinthe *range[-1,1]tovaluesintherange[-90,90],soweshouldrejectany *inputsoutsidethatrangeandtheresultwillalwaysbefinite.
*/ if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
if (arg1 >= 0.0)
result = asind_q1(arg1); else
result = -asind_q1(-arg1);
if (unlikely(isinf(result)))
float_overflow_error();
/*
* sind_q1 - returns the sine of an angle in the first quadrant
* (0 to 90 degrees).
*/
static double
sind_q1(double x)
{
/*
* Stitch together the sine and cosine functions for the ranges [0, 30]
* and (30, 90]. These guarantee to return exact answers at their
* endpoints, so the overall result is a continuous monotonic function
* that gives exact results when x = 0, 30 and 90 degrees.
*/
if (x <= 30.0)
return sind_0_to_30(x);
else
return cosd_0_to_60(90.0 - x);
}
/*
* cosd_q1 - returns the cosine of an angle in the first quadrant
* (0 to 90 degrees).
*/
static double
cosd_q1(double x)
{
/*
* Stitch together the sine and cosine functions for the ranges [0, 60]
* and (60, 90]. These guarantee to return exact answers at their
* endpoints, so the overall result is a continuous monotonic function
* that gives exact results when x = 0, 60 and 90 degrees.
*/
if (x <= 60.0)
return cosd_0_to_60(x);
else
return sind_0_to_30(90.0 - x);
}
/*
* dcosd - returns the cosine of arg1 (degrees)
*/
Datum
dcosd(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
int sign = 1;
/*
* Per the POSIX spec, return NaN if the input is NaN and throw an error
* if the input is infinite.
*/
if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
if (isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
INIT_DEGREE_CONSTANTS();
/* Reduce the range of the input to [0,90] degrees */
arg1 = fmod(arg1, 360.0);
if (unlikely(isinf(result)))
float_overflow_error();
PG_RETURN_FLOAT8(result);
}
/*
* dcotd - returns the cotangent of arg1 (degrees)
*/
Datum
dcotd(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
volatile float8 cot_arg1;
int sign = 1;
/*
* Per the POSIX spec, return NaN if the input is NaN and throw an error
* if the input is infinite.
*/
if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
if (isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
INIT_DEGREE_CONSTANTS();
/* Reduce the range of the input to [0,90] degrees */
arg1 = fmod(arg1, 360.0);
/*
* On some machines we get cotd(270) = minus zero, but this isn't always
* true. For portability, and because the user constituency for this
* function probably doesn't want minus zero, force it to plain zero.
*/
if (result == 0.0)
result = 0.0;
/* Not checking for overflow because cotd(0) == Inf */
PG_RETURN_FLOAT8(result);
}
/*
* dsind - returns the sine of arg1 (degrees)
*/
Datum
dsind(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
int sign = 1;
/*
* Per the POSIX spec, return NaN if the input is NaN and throw an error
* if the input is infinite.
*/
if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
if (isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
INIT_DEGREE_CONSTANTS();
/* Reduce the range of the input to [0,90] degrees */
arg1 = fmod(arg1, 360.0);
if (unlikely(isinf(result)))
float_overflow_error();
PG_RETURN_FLOAT8(result);
}
/*
* dtand - returns the tangent of arg1 (degrees)
*/
Datum
dtand(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
volatile float8 tan_arg1;
int sign = 1;
/*
* Per the POSIX spec, return NaN if the input is NaN and throw an error
* if the input is infinite.
*/
if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
if (isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
INIT_DEGREE_CONSTANTS();
/* Reduce the range of the input to [0,90] degrees */
arg1 = fmod(arg1, 360.0);
/*
* On some machines we get tand(180) = minus zero, but this isn't always
* true. For portability, and because the user constituency for this
* function probably doesn't want minus zero, force it to plain zero.
*/
if (result == 0.0)
result = 0.0;
/* Not checking for overflow because tand(90) == Inf */
PG_RETURN_FLOAT8(result);
}
/*
* degrees - returns degrees converted from radians
*/
Datum
degrees(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
/*
* dsinh - returns the hyperbolic sine of arg1
*/
Datum
dsinh(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
errno = 0;
result = sinh(arg1);
/*
* if an ERANGE error occurs, it means there is an overflow. For sinh,
* the result should be either -infinity or infinity, depending on the
* sign of arg1.
*/
if (errno == ERANGE)
{
if (arg1 < 0)
result = -get_float8_infinity();
else
result = get_float8_infinity();
}
PG_RETURN_FLOAT8(result);
}
/*
* dcosh - returns the hyperbolic cosine of arg1
*/
Datum
dcosh(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
errno = 0;
result = cosh(arg1);
/*
* if an ERANGE error occurs, it means there is an overflow. As cosh is
* always positive, it always means the result is positive infinity.
*/
if (errno == ERANGE)
result = get_float8_infinity();
if (unlikely(result == 0.0))
float_underflow_error();
PG_RETURN_FLOAT8(result);
}
/*
* dtanh - returns the hyperbolic tangent of arg1
*/
Datum
dtanh(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
/*
* For tanh, we don't need an errno check because it never overflows.
*/
result = tanh(arg1);
if (unlikely(isinf(result)))
float_overflow_error();
PG_RETURN_FLOAT8(result);
}
/*
* dasinh - returns the inverse hyperbolic sine of arg1
*/
Datum
dasinh(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
/*
* For asinh, we don't need an errno check because it never overflows.
*/
result = asinh(arg1);
PG_RETURN_FLOAT8(result);
}
/*
* dacosh - returns the inverse hyperbolic cosine of arg1
*/
Datum
dacosh(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
/*
* acosh is only defined for inputs >= 1.0. By checking this ourselves,
* we need not worry about checking for an EDOM error, which is a good
* thing because some implementations will report that for NaN. Otherwise,
* no error is possible.
*/
if (arg1 < 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
result = acosh(arg1);
PG_RETURN_FLOAT8(result);
}
/*
* datanh - returns the inverse hyperbolic tangent of arg1
*/
Datum
datanh(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
/*
* atanh is only defined for inputs between -1 and 1. By checking this
* ourselves, we need not worry about checking for an EDOM error, which is
* a good thing because some implementations will report that for NaN.
*/
if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
/*
* Also handle the infinity cases ourselves; this is helpful because old
* glibc versions may produce the wrong errno for this. All other inputs
* cannot produce an error.
*/
if (arg1 == -1.0)
result = -get_float8_infinity();
else if (arg1 == 1.0)
result = get_float8_infinity();
else
result = atanh(arg1);
PG_RETURN_FLOAT8(result);
}
/* ========== ERROR FUNCTIONS ========== */
/*
* derf - returns the error function: erf(arg1)
*/
Datum
derf(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
/*
* For erf, we don't need an errno check because it never overflows.
*/
result = erf(arg1);
if (unlikely(isinf(result)))
float_overflow_error();
/*
* For erfc, we don't need an errno check because it never overflows.
*/
result = erfc(arg1);
if (unlikely(isinf(result)))
float_overflow_error();
PG_RETURN_FLOAT8(result);
}
/* ========== GAMMA FUNCTIONS ========== */
/*
* dgamma - returns the gamma function of arg1
*/
Datum
dgamma(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
/*
* Handle NaN and Inf cases explicitly. This simplifies the overflow
* checks on platforms that do not set errno.
*/
if (isnan(arg1))
result = arg1;
else if (isinf(arg1))
{
/* Per POSIX, an input of -Inf causes a domain error */
if (arg1 < 0)
{
float_overflow_error();
result = get_float8_nan(); /* keep compiler quiet */
}
else
result = arg1;
}
else
{
/*
* Note: the POSIX/C99 gamma function is called "tgamma", not "gamma".
*
* On some platforms, tgamma() will not set errno but just return Inf,
* NaN, or zero to report overflow/underflow; therefore, test those
* cases explicitly (note that, like the exponential function, the
* gamma function has no zeros).
*/
errno = 0;
result = tgamma(arg1);
if (errno != 0 || isinf(result) || isnan(result))
{
if (result != 0.0)
float_overflow_error();
else
float_underflow_error();
}
else if (result == 0.0)
float_underflow_error();
}
PG_RETURN_FLOAT8(result);
}
/*
* dlgamma - natural logarithm of absolute value of gamma of arg1
*/
Datum
dlgamma(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
/*
* Note: lgamma may not be thread-safe because it may write to a global
* variable signgam, which may not be thread-local. However, this doesn't
* matter to us, since we don't use signgam.
*/
errno = 0;
result = lgamma(arg1);
/*
* If an ERANGE error occurs, it means there was an overflow or a pole
* error (which happens for zero and negative integer inputs).
*
* On some platforms, lgamma() will not set errno but just return infinity
* to report overflow, but it should never underflow.
*/
if (errno == ERANGE || (isinf(result) && !isinf(arg1)))
float_overflow_error();
PG_RETURN_FLOAT8(result);
}
/*
* =========================
* FLOAT AGGREGATE OPERATORS
* =========================
*
* float8_accum - accumulate for AVG(), variance aggregates, etc.
* float4_accum - same, but input data is float4
* float8_avg - produce final result for float AVG()
* float8_var_samp - produce final result for float VAR_SAMP()
* float8_var_pop - produce final result for float VAR_POP()
* float8_stddev_samp - produce final result for float STDDEV_SAMP()
* float8_stddev_pop - produce final result for float STDDEV_POP()
*
* The naive schoolbook implementation of these aggregates works by
* accumulating sum(X) and sum(X^2). However, this approach suffers from
* large rounding errors in the final computation of quantities like the
* population variance (N*sum(X^2) - sum(X)^2) / N^2, since each of the
* intermediate terms is potentially very large, while the difference is often
* quite small.
*
* Instead we use the Youngs-Cramer algorithm [1] which works by accumulating
* Sx=sum(X) and Sxx=sum((X-Sx/N)^2), using a numerically stable algorithm to
* incrementally update those quantities. The final computations of each of
* the aggregate values is then trivial and gives more accurate results (for
* example, the population variance is just Sxx/N). This algorithm is also
* fairly easy to generalize to allow parallel execution without loss of
* precision (see, for example, [2]). For more details, and a comparison of
* this with other algorithms, see [3].
*
* The transition datatype for all these aggregates is a 3-element array
* of float8, holding the values N, Sx, Sxx in that order.
*
* Note that we represent N as a float to avoid having to build a special
* datatype. Given a reasonable floating-point implementation, there should
* be no accuracy loss unless N exceeds 2 ^ 52 or so (by which time the
* user will have doubtless lost interest anyway...)
*
* [1] Some Results Relevant to Choice of Sum and Sum-of-Product Algorithms,
* E. A. Youngs and E. M. Cramer, Technometrics Vol 13, No 3, August 1971.
*
* [2] Updating Formulae and a Pairwise Algorithm for Computing Sample
* Variances, T. F. Chan, G. H. Golub & R. J. LeVeque, COMPSTAT 1982.
*
* [3] Numerically Stable Parallel Computation of (Co-)Variance, Erich
* Schubert and Michael Gertz, Proceedings of the 30th International
* Conference on Scientific and Statistical Database Management, 2018.
*/
static float8 *
check_float8_array(ArrayType *transarray, const char *caller, int n)
{
/*
* We expect the input to be an N-element float array; verify that. We
* don't need to use deconstruct_array() since the array data is just
* going to look like a C array of N float8 values.
*/
if (ARR_NDIM(transarray) != 1 ||
ARR_DIMS(transarray)[0] != n ||
ARR_HASNULL(transarray) ||
ARR_ELEMTYPE(transarray) != FLOAT8OID)
elog(ERROR, "%s: expected %d-element float8 array", caller, n);
return (float8 *) ARR_DATA_PTR(transarray);
}
/*
* float8_combine
*
* An aggregate combine function used to combine two 3 fields
* aggregate transition data into a single transition data.
* This function is used only in two stage aggregation and
* shouldn't be called outside aggregate context.
*/
Datum
float8_combine(PG_FUNCTION_ARGS)
{
ArrayType *transarray1 = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *transarray2 = PG_GETARG_ARRAYTYPE_P(1);
float8 *transvalues1;
float8 *transvalues2;
float8 N1,
Sx1,
Sxx1,
N2,
Sx2,
Sxx2,
tmp,
N,
Sx,
Sxx;
/*--------------------
* The transition values combine using a generalization of the
* Youngs-Cramer algorithm as follows:
*
* N = N1 + N2
* Sx = Sx1 + Sx2
* Sxx = Sxx1 + Sxx2 + N1 * N2 * (Sx1/N1 - Sx2/N2)^2 / N;
*
* It's worth handling the special cases N1 = 0 and N2 = 0 separately
* since those cases are trivial, and we then don't need to worry about
* division-by-zero errors in the general case.
*--------------------
*/
if (N1 == 0.0)
{
N = N2;
Sx = Sx2;
Sxx = Sxx2;
}
else if (N2 == 0.0)
{
N = N1;
Sx = Sx1;
Sxx = Sxx1;
}
else
{
N = N1 + N2;
Sx = float8_pl(Sx1, Sx2);
tmp = Sx1 / N1 - Sx2 / N2;
Sxx = Sxx1 + Sxx2 + N1 * N2 * tmp * tmp / N;
if (unlikely(isinf(Sxx)) && !isinf(Sxx1) && !isinf(Sxx2))
float_overflow_error();
}
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
transvalues1[0] = N;
transvalues1[1] = Sx;
transvalues1[2] = Sxx;
PG_RETURN_ARRAYTYPE_P(transarray1);
}
else
{
Datum transdatums[3];
ArrayType *result;
/*
* Use the Youngs-Cramer algorithm to incorporate the new value into the
* transition values.
*/
N += 1.0;
Sx += newval;
if (transvalues[0] > 0.0)
{
tmp = newval * N - Sx;
Sxx += tmp * tmp / (N * transvalues[0]);
/*
* Overflow check. We only report an overflow error when finite
* inputs lead to infinite results. Note also that Sxx should be NaN
* if any of the inputs are infinite, so we intentionally prevent Sxx
* from becoming infinite.
*/
if (isinf(Sx) || isinf(Sxx))
{
if (!isinf(transvalues[1]) && !isinf(newval))
float_overflow_error();
Sxx = get_float8_nan();
}
}
else
{
/*
* At the first input, we normally can leave Sxx as 0. However, if
* the first input is Inf or NaN, we'd better force Sxx to NaN;
* otherwise we will falsely report variance zero when there are no
* more inputs.
*/
if (isnan(newval) || isinf(newval))
Sxx = get_float8_nan();
}
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
transvalues[0] = N;
transvalues[1] = Sx;
transvalues[2] = Sxx;
PG_RETURN_ARRAYTYPE_P(transarray);
}
else
{
Datum transdatums[3];
ArrayType *result;
/*
* Use the Youngs-Cramer algorithm to incorporate the new value into the
* transition values.
*/
N += 1.0;
Sx += newval;
if (transvalues[0] > 0.0)
{
tmp = newval * N - Sx;
Sxx += tmp * tmp / (N * transvalues[0]);
/*
* Overflow check. We only report an overflow error when finite
* inputs lead to infinite results. Note also that Sxx should be NaN
* if any of the inputs are infinite, so we intentionally prevent Sxx
* from becoming infinite.
*/
if (isinf(Sx) || isinf(Sxx))
{
if (!isinf(transvalues[1]) && !isinf(newval))
float_overflow_error();
Sxx = get_float8_nan();
}
}
else
{
/*
* At the first input, we normally can leave Sxx as 0. However, if
* the first input is Inf or NaN, we'd better force Sxx to NaN;
* otherwise we will falsely report variance zero when there are no
* more inputs.
*/
if (isnan(newval) || isinf(newval))
Sxx = get_float8_nan();
}
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
transvalues[0] = N;
transvalues[1] = Sx;
transvalues[2] = Sxx;
PG_RETURN_ARRAYTYPE_P(transarray);
}
else
{
Datum transdatums[3];
ArrayType *result;
/* Sample stddev is undefined when N is 0 or 1, so return NULL */
if (N <= 1.0)
PG_RETURN_NULL();
/* Note that Sxx is guaranteed to be non-negative */
PG_RETURN_FLOAT8(sqrt(Sxx / (N - 1.0)));
}
/*
* =========================
* SQL2003 BINARY AGGREGATES
* =========================
*
* As with the preceding aggregates, we use the Youngs-Cramer algorithm to
* reduce rounding errors in the aggregate final functions.
*
* The transition datatype for all these aggregates is a 6-element array of
* float8, holding the values N, Sx=sum(X), Sxx=sum((X-Sx/N)^2), Sy=sum(Y),
* Syy=sum((Y-Sy/N)^2), Sxy=sum((X-Sx/N)*(Y-Sy/N)) in that order.
*
* Note that Y is the first argument to all these aggregates!
*
* It might seem attractive to optimize this by having multiple accumulator
* functions that only calculate the sums actually needed. But on most
* modern machines, a couple of extra floating-point multiplies will be
* insignificant compared to the other per-tuple overhead, so I've chosen
* to minimize code space instead.
*/
transvalues = check_float8_array(transarray, "float8_regr_accum", 6);
N = transvalues[0];
Sx = transvalues[1];
Sxx = transvalues[2];
Sy = transvalues[3];
Syy = transvalues[4];
Sxy = transvalues[5];
/*
* Use the Youngs-Cramer algorithm to incorporate the new values into the
* transition values.
*/
N += 1.0;
Sx += newvalX;
Sy += newvalY;
if (transvalues[0] > 0.0)
{
tmpX = newvalX * N - Sx;
tmpY = newvalY * N - Sy;
scale = 1.0 / (N * transvalues[0]);
Sxx += tmpX * tmpX * scale;
Syy += tmpY * tmpY * scale;
Sxy += tmpX * tmpY * scale;
/*
* Overflow check. We only report an overflow error when finite
* inputs lead to infinite results. Note also that Sxx, Syy and Sxy
* should be NaN if any of the relevant inputs are infinite, so we
* intentionally prevent them from becoming infinite.
*/
if (isinf(Sx) || isinf(Sxx) || isinf(Sy) || isinf(Syy) || isinf(Sxy))
{
if (((isinf(Sx) || isinf(Sxx)) &&
!isinf(transvalues[1]) && !isinf(newvalX)) ||
((isinf(Sy) || isinf(Syy)) &&
!isinf(transvalues[3]) && !isinf(newvalY)) ||
(isinf(Sxy) &&
!isinf(transvalues[1]) && !isinf(newvalX) &&
!isinf(transvalues[3]) && !isinf(newvalY)))
float_overflow_error();
if (isinf(Sxx))
Sxx = get_float8_nan();
if (isinf(Syy))
Syy = get_float8_nan();
if (isinf(Sxy))
Sxy = get_float8_nan();
}
}
else
{
/*
* At the first input, we normally can leave Sxx et al as 0. However,
* if the first input is Inf or NaN, we'd better force the dependent
* sums to NaN; otherwise we will falsely report variance zero when
* there are no more inputs.
*/
if (isnan(newvalX) || isinf(newvalX))
Sxx = Sxy = get_float8_nan();
if (isnan(newvalY) || isinf(newvalY))
Syy = Sxy = get_float8_nan();
}
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
transvalues[0] = N;
transvalues[1] = Sx;
transvalues[2] = Sxx;
transvalues[3] = Sy;
transvalues[4] = Syy;
transvalues[5] = Sxy;
PG_RETURN_ARRAYTYPE_P(transarray);
}
else
{
Datum transdatums[6];
ArrayType *result;
result = construct_array_builtin(transdatums, 6, FLOAT8OID);
PG_RETURN_ARRAYTYPE_P(result);
}
}
/*
* float8_regr_combine
*
* An aggregate combine function used to combine two 6 fields
* aggregate transition data into a single transition data.
* This function is used only in two stage aggregation and
* shouldn't be called outside aggregate context.
*/
Datum
float8_regr_combine(PG_FUNCTION_ARGS)
{
ArrayType *transarray1 = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *transarray2 = PG_GETARG_ARRAYTYPE_P(1);
float8 *transvalues1;
float8 *transvalues2;
float8 N1,
Sx1,
Sxx1,
Sy1,
Syy1,
Sxy1,
N2,
Sx2,
Sxx2,
Sy2,
Syy2,
Sxy2,
tmp1,
tmp2,
N,
Sx,
Sxx,
Sy,
Syy,
Sxy;
/*--------------------
* The transition values combine using a generalization of the
* Youngs-Cramer algorithm as follows:
*
* N = N1 + N2
* Sx = Sx1 + Sx2
* Sxx = Sxx1 + Sxx2 + N1 * N2 * (Sx1/N1 - Sx2/N2)^2 / N
* Sy = Sy1 + Sy2
* Syy = Syy1 + Syy2 + N1 * N2 * (Sy1/N1 - Sy2/N2)^2 / N
* Sxy = Sxy1 + Sxy2 + N1 * N2 * (Sx1/N1 - Sx2/N2) * (Sy1/N1 - Sy2/N2) / N
*
* It's worth handling the special cases N1 = 0 and N2 = 0 separately
* since those cases are trivial, and we then don't need to worry about
* division-by-zero errors in the general case.
*--------------------
*/
if (N1 == 0.0)
{
N = N2;
Sx = Sx2;
Sxx = Sxx2;
Sy = Sy2;
Syy = Syy2;
Sxy = Sxy2;
}
else if (N2 == 0.0)
{
N = N1;
Sx = Sx1;
Sxx = Sxx1;
Sy = Sy1;
Syy = Syy1;
Sxy = Sxy1;
}
else
{
N = N1 + N2;
Sx = float8_pl(Sx1, Sx2);
tmp1 = Sx1 / N1 - Sx2 / N2;
Sxx = Sxx1 + Sxx2 + N1 * N2 * tmp1 * tmp1 / N;
if (unlikely(isinf(Sxx)) && !isinf(Sxx1) && !isinf(Sxx2))
float_overflow_error();
Sy = float8_pl(Sy1, Sy2);
tmp2 = Sy1 / N1 - Sy2 / N2;
Syy = Syy1 + Syy2 + N1 * N2 * tmp2 * tmp2 / N;
if (unlikely(isinf(Syy)) && !isinf(Syy1) && !isinf(Syy2))
float_overflow_error();
Sxy = Sxy1 + Sxy2 + N1 * N2 * tmp1 * tmp2 / N;
if (unlikely(isinf(Sxy)) && !isinf(Sxy1) && !isinf(Sxy2))
float_overflow_error();
}
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
transvalues1[0] = N;
transvalues1[1] = Sx;
transvalues1[2] = Sxx;
transvalues1[3] = Sy;
transvalues1[4] = Syy;
transvalues1[5] = Sxy;
PG_RETURN_ARRAYTYPE_P(transarray1);
}
else
{
Datum transdatums[6];
ArrayType *result;
/*
* Implements the float8 version of the width_bucket() function
* defined by SQL2003. See also width_bucket_numeric().
*
* 'bound1' and 'bound2' are the lower and upper bounds of the
* histogram's range, respectively. 'count' is the number of buckets
* in the histogram. width_bucket() returns an integer indicating the
* bucket number that 'operand' belongs to in an equiwidth histogram
* with the specified characteristics. An operand smaller than the
* lower bound is assigned to bucket 0. An operand greater than or equal
* to the upper bound is assigned to an additional bucket (with number
* count+1). We don't allow "NaN" for any of the float8 inputs, and we
* don't allow either of the histogram bounds to be +/- infinity.
*/
Datum
width_bucket_float8(PG_FUNCTION_ARGS)
{
float8 operand = PG_GETARG_FLOAT8(0);
float8 bound1 = PG_GETARG_FLOAT8(1);
float8 bound2 = PG_GETARG_FLOAT8(2);
int32 count = PG_GETARG_INT32(3);
int32 result;
if (count <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
errmsg("count must be greater than zero")));
if (isnan(operand) || isnan(bound1) || isnan(bound2))
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
errmsg("operand, lower bound, and upper bound cannot be NaN")));
/* Note that we allow "operand" to be infinite */
if (isinf(bound1) || isinf(bound2))
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
errmsg("lower and upper bounds must be finite")));
if (bound1 < bound2)
{
if (operand < bound1)
result = 0;
else if (operand >= bound2)
{
if (pg_add_s32_overflow(count, 1, &result))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
}
else
{
if (!isinf(bound2 - bound1))
{
/* The quotient is surely in [0,1], so this can't overflow */
result = count * ((operand - bound1) / (bound2 - bound1));
}
else
{
/*
* We get here if bound2 - bound1 overflows DBL_MAX. Since
* both bounds are finite, their difference can't exceed twice
* DBL_MAX; so we can perform the computation without overflow
* by dividing all the inputs by 2. That should be exact too,
* except in the case where a very small operand underflows to
* zero, which would have negligible impact on the result
* given such large bounds.
*/
result = count * ((operand / 2 - bound1 / 2) / (bound2 / 2 - bound1 / 2));
}
/* The quotient could round to 1.0, which would be a lie */
if (result >= count)
result = count - 1;
/* Having done that, we can add 1 without fear of overflow */
result++;
}
}
else if (bound1 > bound2)
{
if (operand > bound1)
result = 0;
else if (operand <= bound2)
{
if (pg_add_s32_overflow(count, 1, &result))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
}
else
{
if (!isinf(bound1 - bound2))
result = count * ((bound1 - operand) / (bound1 - bound2));
else
result = count * ((bound1 / 2 - operand / 2) / (bound1 / 2 - bound2 / 2));
if (result >= count)
result = count - 1;
result++;
}
}
else
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
errmsg("lower bound cannot equal upper bound")));
result = 0; /* keep the compiler quiet */
}
PG_RETURN_INT32(result);
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.135Bemerkung:
(vorverarbeitet am 2026-10-11)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.