aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/adt/float.c56
-rw-r--r--src/include/pg_config.h.in3
-rw-r--r--src/include/pg_config.h.win323
-rw-r--r--src/include/port.h4
-rw-r--r--src/include/port/win32_port.h12
-rw-r--r--src/port/strtof.c123
-rw-r--r--src/test/regress/expected/float4-misrounded-input.out436
-rw-r--r--src/test/regress/expected/float4.out136
-rw-r--r--src/test/regress/resultmap1
-rw-r--r--src/test/regress/sql/float4.sql37
-rw-r--r--src/tools/msvc/Mkvcbuild.pm2
11 files changed, 796 insertions, 17 deletions
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 117ded8d1de..eb111ee2dbf 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -104,13 +104,39 @@ is_infinite(double val)
/*
* float4in - converts "num" to float4
+ *
+ * Note that this code now uses strtof(), where it used to use strtod().
+ *
+ * The motivation for using strtof() is to avoid a double-rounding problem:
+ * for certain decimal inputs, if you round the input correctly to a double,
+ * and then round the double to a float, the result is incorrect in that it
+ * does not match the result of rounding the decimal value to float directly.
+ *
+ * One of the best examples is 7.038531e-26:
+ *
+ * 0xAE43FDp-107 = 7.03853069185120912085...e-26
+ * midpoint 7.03853100000000022281...e-26
+ * 0xAE43FEp-107 = 7.03853130814879132477...e-26
+ *
+ * making 0xAE43FDp-107 the correct float result, but if you do the conversion
+ * via a double, you get
+ *
+ * 0xAE43FD.7FFFFFF8p-107 = 7.03853099999999907487...e-26
+ * midpoint 7.03853099999999964884...e-26
+ * 0xAE43FD.80000000p-107 = 7.03853100000000022281...e-26
+ * 0xAE43FD.80000008p-107 = 7.03853100000000137076...e-26
+ *
+ * so the value rounds to the double exactly on the midpoint between the two
+ * nearest floats, and then rounding again to a float gives the incorrect
+ * result of 0xAE43FEp-107.
+ *
*/
Datum
float4in(PG_FUNCTION_ARGS)
{
char *num = PG_GETARG_CSTRING(0);
char *orig_num;
- double val;
+ float val;
char *endptr;
/*
@@ -135,7 +161,7 @@ float4in(PG_FUNCTION_ARGS)
"real", orig_num)));
errno = 0;
- val = strtod(num, &endptr);
+ val = strtof(num, &endptr);
/* did we not see anything that looks like a double? */
if (endptr == num || errno != 0)
@@ -143,14 +169,14 @@ float4in(PG_FUNCTION_ARGS)
int save_errno = errno;
/*
- * C99 requires that strtod() accept NaN, [+-]Infinity, and [+-]Inf,
+ * C99 requires that strtof() accept NaN, [+-]Infinity, and [+-]Inf,
* but not all platforms support all of these (and some accept them
* but set ERANGE anyway...) Therefore, we check for these inputs
- * ourselves if strtod() fails.
+ * ourselves if strtof() fails.
*
* Note: C99 also requires hexadecimal input as well as some extended
* forms of NaN, but we consider these forms unportable and don't try
- * to support them. You can use 'em if your strtod() takes 'em.
+ * to support them. You can use 'em if your strtof() takes 'em.
*/
if (pg_strncasecmp(num, "NaN", 3) == 0)
{
@@ -195,8 +221,18 @@ float4in(PG_FUNCTION_ARGS)
* precision). We'd prefer not to throw error for that, so try to
* detect whether it's a "real" out-of-range condition by checking
* to see if the result is zero or huge.
+ *
+ * Use isinf() rather than HUGE_VALF on VS2013 because it generates
+ * a spurious overflow warning for -HUGE_VALF. Also use isinf() if
+ * HUGE_VALF is missing.
*/
- if (val == 0.0 || val >= HUGE_VAL || val <= -HUGE_VAL)
+ if (val == 0.0 ||
+#if !defined(HUGE_VALF) || (defined(_MSC_VER) && (_MSC_VER < 1900))
+ isinf(val)
+#else
+ (val >= HUGE_VALF || val <= -HUGE_VALF)
+#endif
+ )
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("\"%s\" is out of range for type real",
@@ -232,13 +268,7 @@ float4in(PG_FUNCTION_ARGS)
errmsg("invalid input syntax for type %s: \"%s\"",
"real", orig_num)));
- /*
- * if we get here, we have a legal double, still need to check to see if
- * it's a legal float4
- */
- check_float4_val((float4) val, isinf(val), val == 0);
-
- PG_RETURN_FLOAT4((float4) val);
+ PG_RETURN_FLOAT4(val);
}
/*
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 82547f321f3..b38b0ae1899 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -555,6 +555,9 @@
/* Define to 1 if you have the `strsignal' function. */
#undef HAVE_STRSIGNAL
+/* Define to 1 if you have the `strtof' function. */
+#undef HAVE_STRTOF
+
/* Define to 1 if you have the `strtoll' function. */
#undef HAVE_STRTOLL
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index a3c44f0fd80..160fa1279e4 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -139,6 +139,9 @@
don't. */
#define HAVE_DECL_STRNLEN 1
+/* Define to 1 if you have the `strtof' function. */
+#define HAVE_STRTOF 1
+
/* Define to 1 if you have the declaration of `strtoll', and to 0 if you
don't. */
#define HAVE_DECL_STRTOLL 1
diff --git a/src/include/port.h b/src/include/port.h
index 485e771f99f..436bc83fe38 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -381,6 +381,10 @@ extern int isinf(double x);
#endif /* __clang__ && !__cplusplus */
#endif /* !HAVE_ISINF */
+#ifndef HAVE_STRTOF
+extern float strtof(const char *nptr, char **endptr);
+#endif
+
#ifndef HAVE_MKDTEMP
extern char *mkdtemp(char *path);
#endif
diff --git a/src/include/port/win32_port.h b/src/include/port/win32_port.h
index fdd8b2d3b15..9bab75d5da0 100644
--- a/src/include/port/win32_port.h
+++ b/src/include/port/win32_port.h
@@ -510,6 +510,18 @@ typedef unsigned short mode_t;
#define isnan(x) _isnan(x)
#endif
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
+/*
+ * VS2013 has a strtof() that seems to give correct answers for valid input,
+ * even on the rounding edge cases, but which doesn't handle out-of-range
+ * input correctly. Work around that.
+ */
+#define HAVE_BUGGY_WINDOWS_STRTOF 1
+extern float pg_strtof(const char *nptr, char **endptr);
+#define strtof(a,b) (pg_strtof((a),(b)))
+
+#endif
+
/* Pulled from Makefile.port in MinGW */
#define DLSUFFIX ".dll"
diff --git a/src/port/strtof.c b/src/port/strtof.c
new file mode 100644
index 00000000000..7aca8e2e3df
--- /dev/null
+++ b/src/port/strtof.c
@@ -0,0 +1,123 @@
+/*-------------------------------------------------------------------------
+ *
+ * strtof.c
+ *
+ * Portions Copyright (c) 2019, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/port/strtof.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "c.h"
+
+#include <float.h>
+#include <math.h>
+
+#ifndef HAVE_STRTOF
+/*
+ * strtof() is part of C99; this version is only for the benefit of obsolete
+ * platforms. As such, it is known to return incorrect values for edge cases,
+ * which have to be allowed for in variant files for regression test results
+ * for any such platform.
+ */
+
+float
+strtof(const char *nptr, char **endptr)
+{
+ int caller_errno = errno;
+ double dresult;
+ float fresult;
+
+ errno = 0;
+ dresult = strtod(nptr, endptr);
+ fresult = (float) dresult;
+
+ if (errno == 0)
+ {
+ /*
+ * Value might be in-range for double but not float.
+ */
+ if (dresult != 0 && fresult == 0)
+ caller_errno = ERANGE; /* underflow */
+ if (!isinf(dresult) && isinf(fresult))
+ caller_errno = ERANGE; /* overflow */
+ }
+ else
+ caller_errno = errno;
+
+ errno = caller_errno;
+ return fresult;
+}
+
+#elif HAVE_BUGGY_WINDOWS_STRTOF
+/*
+ * On Windows, there's a slightly different problem: VS2013 has a strtof()
+ * that returns the correct results for valid input, but may fail to report an
+ * error for underflow or overflow, returning 0 instead. Work around that by
+ * trying strtod() when strtof() returns 0.0 or [+-]Inf, and calling it an
+ * error if the result differs. Also, strtof() doesn't handle subnormal input
+ * well, so prefer to round the strtod() result in such cases. (Normally we'd
+ * just say "too bad" if strtof() doesn't support subnormals, but since we're
+ * already in here fixing stuff, we might as well do the best fix we can.)
+ */
+float
+pg_strtof(const char *nptr, char **endptr)
+{
+ int caller_errno = errno;
+ float fresult;
+
+ errno = 0;
+ fresult = (strtof)(nptr, endptr);
+ if (errno)
+ {
+ /* On error, just return the error to the caller. */
+ return fresult;
+ }
+ else if ((*endptr == nptr) || isnan(fresult) ||
+ ((fresult >= FLT_MIN || fresult <= -FLT_MIN) && !isinf(fresult)))
+ {
+ /*
+ * If we got nothing parseable, or if we got a non-0 non-subnormal
+ * finite value (or NaN) without error, then return that to the caller
+ * without error.
+ */
+ errno = caller_errno;
+ return fresult;
+ }
+ else
+ {
+ /*
+ * Try again. errno is already 0 here.
+ */
+ double dresult = strtod(nptr, NULL);
+ if (errno)
+ {
+ /* On error, just return the error */
+ return fresult;
+ }
+ else if ((dresult == 0.0 && fresult == 0.0) ||
+ (isinf(dresult) && isinf(fresult) && (fresult == dresult)))
+ {
+ /* both values are 0 or infinities of the same sign */
+ errno = caller_errno;
+ return fresult;
+ }
+ else if ((dresult > 0 && dresult <= FLT_MIN && (float)dresult != 0.0) ||
+ (dresult < 0 && dresult >= -FLT_MIN && (float)dresult != 0.0))
+ {
+ /* subnormal but nonzero value */
+ errno = caller_errno;
+ return (float) dresult;
+ }
+ else
+ {
+ errno = ERANGE;
+ return fresult;
+ }
+ }
+}
+
+#endif
diff --git a/src/test/regress/expected/float4-misrounded-input.out b/src/test/regress/expected/float4-misrounded-input.out
new file mode 100644
index 00000000000..9898d92928d
--- /dev/null
+++ b/src/test/regress/expected/float4-misrounded-input.out
@@ -0,0 +1,436 @@
+--
+-- FLOAT4
+--
+CREATE TABLE FLOAT4_TBL (f1 float4);
+INSERT INTO FLOAT4_TBL(f1) VALUES (' 0.0');
+INSERT INTO FLOAT4_TBL(f1) VALUES ('1004.30 ');
+INSERT INTO FLOAT4_TBL(f1) VALUES (' -34.84 ');
+INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e+20');
+INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e-20');
+-- test for over and under flow
+INSERT INTO FLOAT4_TBL(f1) VALUES ('10e70');
+ERROR: "10e70" is out of range for type real
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('10e70');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e70');
+ERROR: "-10e70" is out of range for type real
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e70');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-70');
+ERROR: "10e-70" is out of range for type real
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-70');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-70');
+ERROR: "-10e-70" is out of range for type real
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-70');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('10e400');
+ERROR: "10e400" is out of range for type real
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('10e400');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e400');
+ERROR: "-10e400" is out of range for type real
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e400');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-400');
+ERROR: "10e-400" is out of range for type real
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-400');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-400');
+ERROR: "-10e-400" is out of range for type real
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-400');
+ ^
+-- bad input
+INSERT INTO FLOAT4_TBL(f1) VALUES ('');
+ERROR: invalid input syntax for type real: ""
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES (' ');
+ERROR: invalid input syntax for type real: " "
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES (' ');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('xyz');
+ERROR: invalid input syntax for type real: "xyz"
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('xyz');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('5.0.0');
+ERROR: invalid input syntax for type real: "5.0.0"
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('5.0.0');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('5 . 0');
+ERROR: invalid input syntax for type real: "5 . 0"
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('5 . 0');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('5. 0');
+ERROR: invalid input syntax for type real: "5. 0"
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('5. 0');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES (' - 3.0');
+ERROR: invalid input syntax for type real: " - 3.0"
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES (' - 3.0');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('123 5');
+ERROR: invalid input syntax for type real: "123 5"
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('123 5');
+ ^
+-- special inputs
+SELECT 'NaN'::float4;
+ float4
+--------
+ NaN
+(1 row)
+
+SELECT 'nan'::float4;
+ float4
+--------
+ NaN
+(1 row)
+
+SELECT ' NAN '::float4;
+ float4
+--------
+ NaN
+(1 row)
+
+SELECT 'infinity'::float4;
+ float4
+----------
+ Infinity
+(1 row)
+
+SELECT ' -INFINiTY '::float4;
+ float4
+-----------
+ -Infinity
+(1 row)
+
+-- bad special inputs
+SELECT 'N A N'::float4;
+ERROR: invalid input syntax for type real: "N A N"
+LINE 1: SELECT 'N A N'::float4;
+ ^
+SELECT 'NaN x'::float4;
+ERROR: invalid input syntax for type real: "NaN x"
+LINE 1: SELECT 'NaN x'::float4;
+ ^
+SELECT ' INFINITY x'::float4;
+ERROR: invalid input syntax for type real: " INFINITY x"
+LINE 1: SELECT ' INFINITY x'::float4;
+ ^
+SELECT 'Infinity'::float4 + 100.0;
+ ?column?
+----------
+ Infinity
+(1 row)
+
+SELECT 'Infinity'::float4 / 'Infinity'::float4;
+ ?column?
+----------
+ NaN
+(1 row)
+
+SELECT 'nan'::float4 / 'nan'::float4;
+ ?column?
+----------
+ NaN
+(1 row)
+
+SELECT 'nan'::numeric::float4;
+ float4
+--------
+ NaN
+(1 row)
+
+SELECT '' AS five, * FROM FLOAT4_TBL;
+ five | f1
+------+-------------
+ | 0
+ | 1004.3
+ | -34.84
+ | 1.23457e+20
+ | 1.23457e-20
+(5 rows)
+
+SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE f.f1 <> '1004.3';
+ four | f1
+------+-------------
+ | 0
+ | -34.84
+ | 1.23457e+20
+ | 1.23457e-20
+(4 rows)
+
+SELECT '' AS one, f.* FROM FLOAT4_TBL f WHERE f.f1 = '1004.3';
+ one | f1
+-----+--------
+ | 1004.3
+(1 row)
+
+SELECT '' AS three, f.* FROM FLOAT4_TBL f WHERE '1004.3' > f.f1;
+ three | f1
+-------+-------------
+ | 0
+ | -34.84
+ | 1.23457e-20
+(3 rows)
+
+SELECT '' AS three, f.* FROM FLOAT4_TBL f WHERE f.f1 < '1004.3';
+ three | f1
+-------+-------------
+ | 0
+ | -34.84
+ | 1.23457e-20
+(3 rows)
+
+SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE '1004.3' >= f.f1;
+ four | f1
+------+-------------
+ | 0
+ | 1004.3
+ | -34.84
+ | 1.23457e-20
+(4 rows)
+
+SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE f.f1 <= '1004.3';
+ four | f1
+------+-------------
+ | 0
+ | 1004.3
+ | -34.84
+ | 1.23457e-20
+(4 rows)
+
+SELECT '' AS three, f.f1, f.f1 * '-10' AS x FROM FLOAT4_TBL f
+ WHERE f.f1 > '0.0';
+ three | f1 | x
+-------+-------------+--------------
+ | 1004.3 | -10043
+ | 1.23457e+20 | -1.23457e+21
+ | 1.23457e-20 | -1.23457e-19
+(3 rows)
+
+SELECT '' AS three, f.f1, f.f1 + '-10' AS x FROM FLOAT4_TBL f
+ WHERE f.f1 > '0.0';
+ three | f1 | x
+-------+-------------+-------------
+ | 1004.3 | 994.3
+ | 1.23457e+20 | 1.23457e+20
+ | 1.23457e-20 | -10
+(3 rows)
+
+SELECT '' AS three, f.f1, f.f1 / '-10' AS x FROM FLOAT4_TBL f
+ WHERE f.f1 > '0.0';
+ three | f1 | x
+-------+-------------+--------------
+ | 1004.3 | -100.43
+ | 1.23457e+20 | -1.23457e+19
+ | 1.23457e-20 | -1.23457e-21
+(3 rows)
+
+SELECT '' AS three, f.f1, f.f1 - '-10' AS x FROM FLOAT4_TBL f
+ WHERE f.f1 > '0.0';
+ three | f1 | x
+-------+-------------+-------------
+ | 1004.3 | 1014.3
+ | 1.23457e+20 | 1.23457e+20
+ | 1.23457e-20 | 10
+(3 rows)
+
+-- test divide by zero
+SELECT '' AS bad, f.f1 / '0.0' from FLOAT4_TBL f;
+ERROR: division by zero
+SELECT '' AS five, * FROM FLOAT4_TBL;
+ five | f1
+------+-------------
+ | 0
+ | 1004.3
+ | -34.84
+ | 1.23457e+20
+ | 1.23457e-20
+(5 rows)
+
+-- test the unary float4abs operator
+SELECT '' AS five, f.f1, @f.f1 AS abs_f1 FROM FLOAT4_TBL f;
+ five | f1 | abs_f1
+------+-------------+-------------
+ | 0 | 0
+ | 1004.3 | 1004.3
+ | -34.84 | 34.84
+ | 1.23457e+20 | 1.23457e+20
+ | 1.23457e-20 | 1.23457e-20
+(5 rows)
+
+UPDATE FLOAT4_TBL
+ SET f1 = FLOAT4_TBL.f1 * '-1'
+ WHERE FLOAT4_TBL.f1 > '0.0';
+SELECT '' AS five, * FROM FLOAT4_TBL;
+ five | f1
+------+--------------
+ | 0
+ | -34.84
+ | -1004.3
+ | -1.23457e+20
+ | -1.23457e-20
+(5 rows)
+
+-- test edge-case coercions to integer
+SELECT '32767.4'::float4::int2;
+ int2
+-------
+ 32767
+(1 row)
+
+SELECT '32767.6'::float4::int2;
+ERROR: smallint out of range
+SELECT '-32768.4'::float4::int2;
+ int2
+--------
+ -32768
+(1 row)
+
+SELECT '-32768.6'::float4::int2;
+ERROR: smallint out of range
+SELECT '2147483520'::float4::int4;
+ int4
+------------
+ 2147483520
+(1 row)
+
+SELECT '2147483647'::float4::int4;
+ERROR: integer out of range
+SELECT '-2147483648.5'::float4::int4;
+ int4
+-------------
+ -2147483648
+(1 row)
+
+SELECT '-2147483900'::float4::int4;
+ERROR: integer out of range
+SELECT '9223369837831520256'::float4::int8;
+ int8
+---------------------
+ 9223369837831520256
+(1 row)
+
+SELECT '9223372036854775807'::float4::int8;
+ERROR: bigint out of range
+SELECT '-9223372036854775808.5'::float4::int8;
+ int8
+----------------------
+ -9223372036854775808
+(1 row)
+
+SELECT '-9223380000000000000'::float4::int8;
+ERROR: bigint out of range
+-- Test for correct input rounding in edge cases.
+-- These lists are from Paxson 1991, excluding subnormals and
+-- inputs of over 9 sig. digits.
+SELECT float4send('5e-20'::float4);
+ float4send
+------------
+ \x1f6c1e4a
+(1 row)
+
+SELECT float4send('67e14'::float4);
+ float4send
+------------
+ \x59be6cea
+(1 row)
+
+SELECT float4send('985e15'::float4);
+ float4send
+------------
+ \x5d5ab6c4
+(1 row)
+
+SELECT float4send('55895e-16'::float4);
+ float4send
+------------
+ \x2cc4a9bd
+(1 row)
+
+SELECT float4send('7038531e-32'::float4);
+ float4send
+------------
+ \x15ae43fe
+(1 row)
+
+SELECT float4send('702990899e-20'::float4);
+ float4send
+------------
+ \x2cf757ca
+(1 row)
+
+SELECT float4send('3e-23'::float4);
+ float4send
+------------
+ \x1a111234
+(1 row)
+
+SELECT float4send('57e18'::float4);
+ float4send
+------------
+ \x6045c22c
+(1 row)
+
+SELECT float4send('789e-35'::float4);
+ float4send
+------------
+ \x0a23de70
+(1 row)
+
+SELECT float4send('2539e-18'::float4);
+ float4send
+------------
+ \x2736f449
+(1 row)
+
+SELECT float4send('76173e28'::float4);
+ float4send
+------------
+ \x7616398a
+(1 row)
+
+SELECT float4send('887745e-11'::float4);
+ float4send
+------------
+ \x3714f05c
+(1 row)
+
+SELECT float4send('5382571e-37'::float4);
+ float4send
+------------
+ \x0d2eaca7
+(1 row)
+
+SELECT float4send('82381273e-35'::float4);
+ float4send
+------------
+ \x128289d0
+(1 row)
+
+SELECT float4send('750486563e-38'::float4);
+ float4send
+------------
+ \x0f18377e
+(1 row)
+
+-- Test that the smallest possible normalized input value inputs
+-- correctly, either in 9-significant-digit or shortest-decimal
+-- format.
+--
+-- exact val is 1.1754943508...
+-- shortest val is 1.1754944000
+-- midpoint to next val is 1.1754944208...
+SELECT float4send('1.17549435e-38'::float4);
+ float4send
+------------
+ \x00800000
+(1 row)
+
+SELECT float4send('1.1754944e-38'::float4);
+ float4send
+------------
+ \x00800000
+(1 row)
+
diff --git a/src/test/regress/expected/float4.out b/src/test/regress/expected/float4.out
index 2f47e1c202a..9b8b4d4280d 100644
--- a/src/test/regress/expected/float4.out
+++ b/src/test/regress/expected/float4.out
@@ -9,21 +9,37 @@ INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e+20');
INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e-20');
-- test for over and under flow
INSERT INTO FLOAT4_TBL(f1) VALUES ('10e70');
-ERROR: value out of range: overflow
+ERROR: "10e70" is out of range for type real
LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('10e70');
^
INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e70');
-ERROR: value out of range: overflow
+ERROR: "-10e70" is out of range for type real
LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e70');
^
INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-70');
-ERROR: value out of range: underflow
+ERROR: "10e-70" is out of range for type real
LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-70');
^
INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-70');
-ERROR: value out of range: underflow
+ERROR: "-10e-70" is out of range for type real
LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-70');
^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('10e400');
+ERROR: "10e400" is out of range for type real
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('10e400');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e400');
+ERROR: "-10e400" is out of range for type real
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e400');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-400');
+ERROR: "10e-400" is out of range for type real
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-400');
+ ^
+INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-400');
+ERROR: "-10e-400" is out of range for type real
+LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-400');
+ ^
-- bad input
INSERT INTO FLOAT4_TBL(f1) VALUES ('');
ERROR: invalid input syntax for type real: ""
@@ -306,3 +322,115 @@ SELECT '-9223372036854775808.5'::float4::int8;
SELECT '-9223380000000000000'::float4::int8;
ERROR: bigint out of range
+-- Test for correct input rounding in edge cases.
+-- These lists are from Paxson 1991, excluding subnormals and
+-- inputs of over 9 sig. digits.
+SELECT float4send('5e-20'::float4);
+ float4send
+------------
+ \x1f6c1e4a
+(1 row)
+
+SELECT float4send('67e14'::float4);
+ float4send
+------------
+ \x59be6cea
+(1 row)
+
+SELECT float4send('985e15'::float4);
+ float4send
+------------
+ \x5d5ab6c4
+(1 row)
+
+SELECT float4send('55895e-16'::float4);
+ float4send
+------------
+ \x2cc4a9bd
+(1 row)
+
+SELECT float4send('7038531e-32'::float4);
+ float4send
+------------
+ \x15ae43fd
+(1 row)
+
+SELECT float4send('702990899e-20'::float4);
+ float4send
+------------
+ \x2cf757ca
+(1 row)
+
+SELECT float4send('3e-23'::float4);
+ float4send
+------------
+ \x1a111234
+(1 row)
+
+SELECT float4send('57e18'::float4);
+ float4send
+------------
+ \x6045c22c
+(1 row)
+
+SELECT float4send('789e-35'::float4);
+ float4send
+------------
+ \x0a23de70
+(1 row)
+
+SELECT float4send('2539e-18'::float4);
+ float4send
+------------
+ \x2736f449
+(1 row)
+
+SELECT float4send('76173e28'::float4);
+ float4send
+------------
+ \x7616398a
+(1 row)
+
+SELECT float4send('887745e-11'::float4);
+ float4send
+------------
+ \x3714f05c
+(1 row)
+
+SELECT float4send('5382571e-37'::float4);
+ float4send
+------------
+ \x0d2eaca7
+(1 row)
+
+SELECT float4send('82381273e-35'::float4);
+ float4send
+------------
+ \x128289d1
+(1 row)
+
+SELECT float4send('750486563e-38'::float4);
+ float4send
+------------
+ \x0f18377e
+(1 row)
+
+-- Test that the smallest possible normalized input value inputs
+-- correctly, either in 9-significant-digit or shortest-decimal
+-- format.
+--
+-- exact val is 1.1754943508...
+-- shortest val is 1.1754944000
+-- midpoint to next val is 1.1754944208...
+SELECT float4send('1.17549435e-38'::float4);
+ float4send
+------------
+ \x00800000
+(1 row)
+
+SELECT float4send('1.1754944e-38'::float4);
+ float4send
+------------
+ \x00800000
+(1 row)
+
diff --git a/src/test/regress/resultmap b/src/test/regress/resultmap
index 46ca5639c23..3bd1585a355 100644
--- a/src/test/regress/resultmap
+++ b/src/test/regress/resultmap
@@ -3,3 +3,4 @@ float8:out:i.86-.*-openbsd=float8-small-is-zero.out
float8:out:i.86-.*-netbsd=float8-small-is-zero.out
float8:out:m68k-.*-netbsd=float8-small-is-zero.out
float8:out:i.86-pc-cygwin=float8-small-is-zero.out
+float4:out:hppa.*-hp-hpux10.*=float4-misrounded-input.out
diff --git a/src/test/regress/sql/float4.sql b/src/test/regress/sql/float4.sql
index 46a9166d131..1fc482e146d 100644
--- a/src/test/regress/sql/float4.sql
+++ b/src/test/regress/sql/float4.sql
@@ -16,6 +16,11 @@ INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e70');
INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-70');
INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-70');
+INSERT INTO FLOAT4_TBL(f1) VALUES ('10e400');
+INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e400');
+INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-400');
+INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-400');
+
-- bad input
INSERT INTO FLOAT4_TBL(f1) VALUES ('');
INSERT INTO FLOAT4_TBL(f1) VALUES (' ');
@@ -95,3 +100,35 @@ SELECT '9223369837831520256'::float4::int8;
SELECT '9223372036854775807'::float4::int8;
SELECT '-9223372036854775808.5'::float4::int8;
SELECT '-9223380000000000000'::float4::int8;
+
+-- Test for correct input rounding in edge cases.
+-- These lists are from Paxson 1991, excluding subnormals and
+-- inputs of over 9 sig. digits.
+
+SELECT float4send('5e-20'::float4);
+SELECT float4send('67e14'::float4);
+SELECT float4send('985e15'::float4);
+SELECT float4send('55895e-16'::float4);
+SELECT float4send('7038531e-32'::float4);
+SELECT float4send('702990899e-20'::float4);
+
+SELECT float4send('3e-23'::float4);
+SELECT float4send('57e18'::float4);
+SELECT float4send('789e-35'::float4);
+SELECT float4send('2539e-18'::float4);
+SELECT float4send('76173e28'::float4);
+SELECT float4send('887745e-11'::float4);
+SELECT float4send('5382571e-37'::float4);
+SELECT float4send('82381273e-35'::float4);
+SELECT float4send('750486563e-38'::float4);
+
+-- Test that the smallest possible normalized input value inputs
+-- correctly, either in 9-significant-digit or shortest-decimal
+-- format.
+--
+-- exact val is 1.1754943508...
+-- shortest val is 1.1754944000
+-- midpoint to next val is 1.1754944208...
+
+SELECT float4send('1.17549435e-38'::float4);
+SELECT float4send('1.1754944e-38'::float4);
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 56192f1b20c..c930eafe890 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -105,6 +105,8 @@ sub mkvcbuild
push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00');
+ push(@pgportfiles, 'strtof.c') if ($vsVersion < '14.00');
+
if ($vsVersion >= '9.00')
{
push(@pgportfiles, 'pg_crc32c_sse42_choose.c');