PostgreSQL Source Code  git master
strtof.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * strtof.c
4  *
5  * Portions Copyright (c) 2019-2020, PostgreSQL Global Development Group
6  *
7  *
8  * IDENTIFICATION
9  * src/port/strtof.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 
14 #include "c.h"
15 
16 #include <float.h>
17 #include <math.h>
18 
19 #ifndef HAVE_STRTOF
20 /*
21  * strtof() is part of C99; this version is only for the benefit of obsolete
22  * platforms. As such, it is known to return incorrect values for edge cases,
23  * which have to be allowed for in variant files for regression test results
24  * for any such platform.
25  */
26 
27 float
28 strtof(const char *nptr, char **endptr)
29 {
30  int caller_errno = errno;
31  double dresult;
32  float fresult;
33 
34  errno = 0;
35  dresult = strtod(nptr, endptr);
36  fresult = (float) dresult;
37 
38  if (errno == 0)
39  {
40  /*
41  * Value might be in-range for double but not float.
42  */
43  if (dresult != 0 && fresult == 0)
44  caller_errno = ERANGE; /* underflow */
45  if (!isinf(dresult) && isinf(fresult))
46  caller_errno = ERANGE; /* overflow */
47  }
48  else
49  caller_errno = errno;
50 
51  errno = caller_errno;
52  return fresult;
53 }
54 
55 #elif HAVE_BUGGY_STRTOF
56 /*
57  * On Windows, there's a slightly different problem: VS2013 has a strtof()
58  * that returns the correct results for valid input, but may fail to report an
59  * error for underflow or overflow, returning 0 instead. Work around that by
60  * trying strtod() when strtof() returns 0.0 or [+-]Inf, and calling it an
61  * error if the result differs. Also, strtof() doesn't handle subnormal input
62  * well, so prefer to round the strtod() result in such cases. (Normally we'd
63  * just say "too bad" if strtof() doesn't support subnormals, but since we're
64  * already in here fixing stuff, we might as well do the best fix we can.)
65  *
66  * Cygwin has a strtof() which is literally just (float)strtod(), which means
67  * we can't avoid the double-rounding problem; but using this wrapper does get
68  * us proper over/underflow checks. (Also, if they fix their strtof(), the
69  * wrapper doesn't break anything.)
70  *
71  * Test results on Mingw suggest that it has the same problem, though looking
72  * at the code I can't figure out why.
73  */
74 float
75 pg_strtof(const char *nptr, char **endptr)
76 {
77  int caller_errno = errno;
78  float fresult;
79 
80  errno = 0;
81  fresult = (strtof) (nptr, endptr);
82  if (errno)
83  {
84  /* On error, just return the error to the caller. */
85  return fresult;
86  }
87  else if ((*endptr == nptr) || isnan(fresult) ||
88  ((fresult >= FLT_MIN || fresult <= -FLT_MIN) && !isinf(fresult)))
89  {
90  /*
91  * If we got nothing parseable, or if we got a non-0 non-subnormal
92  * finite value (or NaN) without error, then return that to the caller
93  * without error.
94  */
95  errno = caller_errno;
96  return fresult;
97  }
98  else
99  {
100  /*
101  * Try again. errno is already 0 here.
102  */
103  double dresult = strtod(nptr, NULL);
104 
105  if (errno)
106  {
107  /* On error, just return the error */
108  return fresult;
109  }
110  else if ((dresult == 0.0 && fresult == 0.0) ||
111  (isinf(dresult) && isinf(fresult) && (fresult == dresult)))
112  {
113  /* both values are 0 or infinities of the same sign */
114  errno = caller_errno;
115  return fresult;
116  }
117  else if ((dresult > 0 && dresult <= FLT_MIN && (float) dresult != 0.0) ||
118  (dresult < 0 && dresult >= -FLT_MIN && (float) dresult != 0.0))
119  {
120  /* subnormal but nonzero value */
121  errno = caller_errno;
122  return (float) dresult;
123  }
124  else
125  {
126  errno = ERANGE;
127  return fresult;
128  }
129  }
130 }
131 
132 #endif
float strtof(const char *nptr, char **endptr)
Definition: strtof.c:28