/*------------------------------------------------------------------------- * * strtof.c * * Portions Copyright (c) 2019-2021, PostgreSQL Global Development Group * * * IDENTIFICATION * src/port/strtof.c * *------------------------------------------------------------------------- */ #include "c.h" #include #include #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_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.) * * Cygwin has a strtof() which is literally just (float)strtod(), which means * we can't avoid the double-rounding problem; but using this wrapper does get * us proper over/underflow checks. (Also, if they fix their strtof(), the * wrapper doesn't break anything.) * * Test results on Mingw suggest that it has the same problem, though looking * at the code I can't figure out why. */ 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