libCom: Add routines for parsing strings into epicsTypes

Adds new routines for converting numeric strings into the
standard epicsInt8, epicsUInt8, epicsInt16, epicsUInt16,
epicsInt32 and epicsUInt32 types, along with Long, ULong,
Double and Float.  These all provide error checking and
detection of extraneous characters.  The epicsScanDouble
and epicsScanFloat routines originally in epicsStdlib.h
are replaced by macros that call the epicsParse routine,
and this also provides epicsScanLong and epicsScanULong
to match.

A test file is added to ensure conversions work properly
and report appropriate errors.  This file also checks the
native strtod() routine if not used to check whether the
epicsStrtod() code is required on this platform.
This commit is contained in:
Andrew Johnson
2012-06-22 14:06:52 -05:00
parent 7cae5ebd7b
commit 269fb01c63
7 changed files with 722 additions and 27 deletions

View File

@@ -23,6 +23,7 @@ Com_SRCS += errSymTbl.c
#
ERR_S_FILES += $(LIBCOM)/osi/devLib.h
ERR_S_FILES += $(LIBCOM)/as/asLib.h
ERR_S_FILES += $(LIBCOM)/misc/epicsStdlib.h
ERR_S_FILES += $(SRC)/ioc/db/dbAccessDefs.h
ERR_S_FILES += $(SRC)/ioc/dbStatic/devSup.h
ERR_S_FILES += $(SRC)/ioc/dbStatic/drvSup.h

View File

@@ -48,6 +48,7 @@ extern "C" {
#define M_casApp (524 <<16) /*CA server application*/
#define M_bucket (525 <<16) /*Bucket Hash*/
#define M_gddFuncTbl (526 <<16) /*gdd jump table*/
#define M_stdlib (527 <<16) /*EPICS Standard library*/
epicsShareFunc void epicsShareAPI errSymLookup(long status, char *pBuf, unsigned bufLength);
epicsShareFunc void epicsShareAPI errSymTest(unsigned short modnum, unsigned short begErrNum, unsigned short endErrNum);

View File

@@ -1,50 +1,248 @@
/*************************************************************************\
* Copyright (c) 2009 UChicago Argonna LLC, as Operator of Argonne
* Copyright (c) 2012 UChicago Argonna LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*epicsStdlib.c*/
/*Author: Eric Norum */
/* $Revision-Id$ */
/* Authors: Eric Norum & Andrew Johnson */
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <float.h>
#define epicsExportSharedSymbols
#include "epicsMath.h"
#include "epicsStdlib.h"
#include "epicsString.h"
#include "epicsConvert.h"
epicsShareFunc int epicsScanDouble(const char *str, double *dest)
/* These are the conversion primitives */
epicsShareFunc int
epicsParseLong(const char *str, long *to, int base)
{
int c;
char *endp;
double dtmp;
long value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = strtol(str, &endp, base);
dtmp = epicsStrtod(str, &endp);
if (endp == str)
return 0;
*dest = dtmp;
return 1;
return S_stdlib_noConversion;
if (errno == EINVAL) /* Not universally supported */
return S_stdlib_badBase;
while ((c = *endp) && isspace(c))
++endp;
if (c)
return S_stdlib_extraneous;
if (errno == ERANGE)
return S_stdlib_overflow;
*to = value;
return 0;
}
epicsShareFunc int epicsScanFloat(const char *str, float *dest)
epicsShareFunc int
epicsParseULong(const char *str, unsigned long *to, int base)
{
int c;
char *endp;
double dtmp;
unsigned long value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = strtoul(str, &endp, base);
dtmp = epicsStrtod(str, &endp);
if (endp == str)
return 0;
*dest = (float)dtmp;
return 1;
return S_stdlib_noConversion;
if (errno == EINVAL) /* Not universally supported */
return S_stdlib_badBase;
while ((c = *endp) && isspace(c))
++endp;
if (c)
return S_stdlib_extraneous;
if (errno == ERANGE)
return S_stdlib_overflow;
*to = value;
return 0;
}
/* Systems with a working strtod() just #define epicsStrtod strtod */
epicsShareFunc int
epicsParseDouble(const char *str, double *to)
{
int c;
char *endp;
double value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = epicsStrtod(str, &endp);
if (endp == str)
return S_stdlib_noConversion;
while ((c = *endp) && isspace(c))
++endp;
if (c)
return S_stdlib_extraneous;
if (errno == ERANGE)
return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow;
*to = value;
return 0;
}
/* These call the primitives */
epicsShareFunc int
epicsParseInt8(const char *str, epicsInt8 *to, int base)
{
long value;
int status = epicsParseLong(str, &value, base);
if (status)
return status;
if (value < -0x80 || value > 0x7f)
return S_stdlib_overflow;
*to = value;
return 0;
}
epicsShareFunc int
epicsParseUInt8(const char *str, epicsUInt8 *to, int base)
{
unsigned long value;
int status = epicsParseULong(str, &value, base);
if (status)
return status;
if (value > 0xff)
return S_stdlib_overflow;
*to = value;
return 0;
}
epicsShareFunc int
epicsParseInt16(const char *str, epicsInt16 *to, int base)
{
long value;
int status = epicsParseLong(str, &value, base);
if (status)
return status;
if (value < -0x8000 || value > 0x7fff)
return S_stdlib_overflow;
*to = value;
return 0;
}
epicsShareFunc int
epicsParseUInt16(const char *str, epicsUInt16 *to, int base)
{
unsigned long value;
int status = epicsParseULong(str, &value, base);
if (status)
return status;
if (value > 0xffff)
return S_stdlib_overflow;
*to = value;
return 0;
}
epicsShareFunc int
epicsParseInt32(const char *str, epicsInt32 *to, int base)
{
long value;
int status = epicsParseLong(str, &value, base);
if (status)
return status;
#if (LONG_MAX > 0x7fffffff)
if (value < -0x80000000L || value > 0x7fffffffL)
return S_stdlib_overflow;
#endif
*to = value;
return 0;
}
epicsShareFunc int
epicsParseUInt32(const char *str, epicsUInt32 *to, int base)
{
unsigned long value;
int status = epicsParseULong(str, &value, base);
if (status)
return status;
#if (ULONG_MAX > 0xffffffff)
if (value > 0xffffffffL)
return S_stdlib_overflow;
#endif
*to = value;
return 0;
}
epicsShareFunc int
epicsParseFloat(const char *str, float *to)
{
double value, abs;
int status = epicsParseDouble(str, &value);
if (status)
return status;
abs = fabs(value);
if (value > 0 && abs <= FLT_MIN)
return S_stdlib_underflow;
if (finite(value) && abs >= FLT_MAX)
return S_stdlib_overflow;
*to = value;
return 0;
}
/* If strtod() works properly, osdStrtod.h defines this macro:
* #define epicsStrtod strtod
*
* If strtod() is broken, osdStrtod.h defines this prototype:
* epicsShareFunc double epicsStrtod(const char *str, char **endp);
*/
#ifndef epicsStrtod
epicsShareFunc double epicsStrtod(const char *str, char **endp)
epicsShareFunc double
epicsStrtod(const char *str, char **endp)
{
const char *cp = str;
int negative = 0;
@@ -60,6 +258,12 @@ epicsShareFunc double epicsStrtod(const char *str, char **endp)
cp++;
}
if (epicsStrnCaseCmp("0x", cp, 2) == 0) {
if (negative)
return strtol(str, endp, 16);
else
return strtoul(str, endp, 16);
}
if (!isalpha((int)*cp))
return strtod(str, endp);

View File

@@ -1,26 +1,67 @@
/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*epicsStdlib.h*/
/*Author: Eric Norum */
/* epicsStdlib.h */
/* Author: Eric Norum */
#include <shareLib.h>
#include <stdlib.h>
#include <limits.h>
#include "shareLib.h"
#include "osdStrtod.h"
#include "epicsTypes.h"
#include "errMdef.h"
#ifdef __cplusplus
extern "C" {
#endif
epicsShareFunc int epicsScanDouble(const char *str, double *dest);
epicsShareFunc int epicsScanFloat(const char *str, float *dest);
#define S_stdlib_noConversion (M_stdlib | 1) /* No digits to convert */
#define S_stdlib_extraneous (M_stdlib | 2) /* Extraneous characters */
#define S_stdlib_underflow (M_stdlib | 3) /* Too small to represent */
#define S_stdlib_overflow (M_stdlib | 4) /* Too large to represent */
#define S_stdlib_badBase (M_stdlib | 5) /* Number base not supported */
#include <stdlib.h>
#include <osdStrtod.h>
epicsShareFunc int
epicsParseLong(const char *str, long *to, int base);
epicsShareFunc int
epicsParseULong(const char *str, unsigned long *to, int base);
epicsShareFunc int
epicsParseDouble(const char *str, double *to);
epicsShareFunc int
epicsParseFloat(const char *str, float *to);
epicsShareFunc int
epicsParseInt8(const char *str, epicsInt8 *to, int base);
epicsShareFunc int
epicsParseUInt8(const char *str, epicsUInt8 *to, int base);
epicsShareFunc int
epicsParseInt16(const char *str, epicsInt16 *to, int base);
epicsShareFunc int
epicsParseUInt16(const char *str, epicsUInt16 *to, int base);
epicsShareFunc int
epicsParseInt32(const char *str, epicsInt32 *to, int base);
epicsShareFunc int
epicsParseUInt32(const char *str, epicsUInt32 *to, int base);
#define epicsParseFloat32(str, to) epicsParseFloat(str, to)
#define epicsParseFloat64(str, to) epicsParseDouble(str, to)
/* These macros return 1 if successful, 0 on failure.
* This is analagous to the return value from sscanf()
*/
#define epicsScanLong(str, to, base) !epicsParseLong(str, to, base)
#define epicsScanULong(str, to, base) !epicsParseULong(str, to, base)
#define epicsScanFloat(str, to) !epicsParseFloat(str, to)
#define epicsScanDouble(str, to) !epicsParseDouble(str, to)
#ifdef __cplusplus
}

View File

@@ -54,6 +54,11 @@ epicsStdioTest_SRCS += epicsStdioTest.c
testHarness_SRCS += epicsStdioTest.c
TESTS += epicsStdioTest
TESTPROD_HOST += epicsStdlibTest
epicsStdlibTest_SRCS += epicsStdlibTest.c
testHarness_SRCS += epicsStdlibTest.c
TESTS += epicsStdlibTest
TESTPROD_HOST += epicsStringTest
epicsStringTest_SRCS += epicsStringTest.c
testHarness_SRCS += epicsStringTest.c

View File

@@ -28,6 +28,7 @@ int epicsMathTest(void);
int epicsMessageQueueTest(void);
int epicsMutexTest(void);
int epicsStdioTest(void);
int epicsStdlibTest(void);
int epicsStringTest(void);
int epicsThreadOnceTest(void);
int epicsThreadPriorityTest(void);
@@ -76,6 +77,8 @@ void epicsRunLibComTests(void)
runTest(epicsStdioTest);
runTest(epicsStdlibTest);
runTest(epicsStringTest);
runTest(epicsThreadOnceTest);

View File

@@ -0,0 +1,440 @@
/*************************************************************************\
* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* epicsStdlibTest.c
*
* Author Andrew Johnson
*/
#include <float.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include "epicsStdlib.h"
#include "epicsMath.h"
#include "epicsUnitTest.h"
#include "testMain.h"
/* Implement the epicsParseDouble() API for checking the native stdtod()
* so we can tell the user if our epicsStrtod() wrapper is unnecessary.
*/
int
parseStrtod(const char *str, double *to)
{
int c;
char *endp;
double dtmp;
while ((c = *str) && isspace(c))
++str;
errno = 0;
dtmp = strtod(str, &endp);
if (endp == str)
return S_stdlib_noConversion;
while ((c = *endp) && isspace(c))
++endp;
if (c)
return S_stdlib_extraneous;
if (dtmp == 0 && errno == ERANGE)
return S_stdlib_underflow;
if (fabs(dtmp) == HUGE_VAL && errno == ERANGE)
return S_stdlib_overflow;
*to = dtmp;
return 0;
}
#define scanStrtod(str, to) !parseStrtod(str, to)
MAIN(epicsStdlibTest)
{
unsigned long u;
long l;
double d;
float f;
epicsInt8 i8;
epicsUInt8 u8;
epicsInt16 i16;
epicsUInt16 u16;
epicsInt32 i32;
epicsUInt32 u32;
testPlan(135);
testOk(epicsParseLong("", &l, 0) == S_stdlib_noConversion,
"Long '' => noConversion");
testOk(epicsParseULong("", &u, 0) == S_stdlib_noConversion,
"ULong '' => noConversion");
testOk(epicsParseFloat("", &f) == S_stdlib_noConversion,
"Float '' => noConversion");
testOk(epicsParseDouble("", &d) == S_stdlib_noConversion,
"Double '' => noConversion");
testOk(epicsParseLong("\t \n", &l, 0) == S_stdlib_noConversion,
"Long '\\t 1\\n' => noConversion");
testOk(epicsParseULong("\t \n", &u, 0) == S_stdlib_noConversion,
"ULong '\\t 1\\n' => noConversion");
testOk(epicsParseFloat("\t \n", &f) == S_stdlib_noConversion,
"Float '\\t 1\\n' => noConversion");
testOk(epicsParseDouble("\t \n", &d) == S_stdlib_noConversion,
"Double '\\t 1\\n' => noConversion");
testOk(epicsParseLong("!", &l, 0) == S_stdlib_noConversion,
"Long '!' => noConversion");
testOk(epicsParseULong("!", &u, 0) == S_stdlib_noConversion,
"ULong '!' => noConversion");
testOk(epicsParseFloat("!", &f) == S_stdlib_noConversion,
"Float '!' => noConversion");
testOk(epicsParseDouble("!", &d) == S_stdlib_noConversion,
"Double '!' => noConversion");
testOk(epicsScanLong("0", &l, 0) && l == 0, "Long '0'");
testOk(epicsScanULong("0", &u, 0) && u == 0, "ULong '0'");
testOk(epicsScanFloat("0", &f) && f == 0, "Float '0'");
testOk(epicsScanDouble("0", &d) && d == 0, "Double '0'");
testOk(epicsScanLong("\t 1\n", &l, 0) && l == 1, "Long '\\t 1\\n'");
testOk(epicsScanULong("\t 1\n", &u, 0) && u == 1, "ULong '\\t 1\\n'");
testOk(epicsScanFloat("\t 1\n", &f) && f == 1, "Float '\\t 1\\n'");
testOk(epicsScanDouble("\t 1\n", &d) && d == 1, "Double '\\t 1\\n'");
testOk(epicsParseLong("2!", &l, 0) == S_stdlib_extraneous,
"Long '2!' => extraneous");
testOk(epicsParseULong("2!", &u, 0) == S_stdlib_extraneous,
"ULong '2!' => extraneous");
testOk(epicsParseFloat("2!", &f) == S_stdlib_extraneous,
"Float '2!' => extraneous");
testOk(epicsParseDouble("2!", &d) == S_stdlib_extraneous,
"Double '2!' => extraneous");
testOk(epicsParseLong("3 !", &l, 0) == S_stdlib_extraneous,
"Long '3 !' => extraneous");
testOk(epicsParseULong("3 !", &u, 0) == S_stdlib_extraneous,
"ULong '3 !' => extraneous");
testOk(epicsParseFloat("3 !", &f) == S_stdlib_extraneous,
"Float '3 !' => extraneous");
testOk(epicsParseDouble("3 !", &d) == S_stdlib_extraneous,
"Double '3 !' => extraneous");
testOk(epicsScanLong("0x0", &l, 0) && l == 0, "Long '0x0'");
testOk(epicsScanULong("0x0", &u, 0) && u == 0, "ULong '0x0'");
testOk(epicsScanFloat("0x0", &f) && f == 0, "Float '0x0'");
testOk(epicsScanDouble("0x0", &d) && d == 0, "Double '0x0'");
testOk(epicsScanLong("0x1", &l, 0) && l == 1, "Long '0x1'");
testOk(epicsScanULong("0x1", &u, 0) && u == 1, "ULong '0x1'");
testOk(epicsScanFloat("0x1", &f) && f == 1, "Float '0x1'");
testOk(epicsScanDouble("0x1", &d) && d == 1, "Double '0x1'");
testOk(epicsScanLong("+0x1", &l, 0) && l == 1, "Long '+0x1'");
testOk(epicsScanULong("+0x1", &u, 0) && u == 1, "ULong '+0x1'");
testOk(epicsScanFloat("+0x1", &f) && f == 1, "Float '+0x1'");
testOk(epicsScanDouble("+0x1", &d) && d == 1, "Double '+0x1'");
testOk(epicsScanLong("-0x1", &l, 0) && l == -1, "Long '-0x1'");
testOk(epicsScanULong("-0x1", &u, 0) && u == -1, "ULong '-0x1'");
testOk(epicsScanFloat("-0x1", &f) && f == -1, "Float '-0x1'");
testOk(epicsScanDouble("-0x1", &d) && d == -1, "Double '-0x1'");
testOk(epicsScanLong("0xf", &l, 0) && l == 15, "Long '0xf'");
testOk(epicsScanULong("0xf", &u, 0) && u == 15, "ULong '0xf'");
testOk(epicsScanFloat("0xf", &f) && f == 15, "Float '0xf'");
testOk(epicsScanDouble("0xf", &d) && d == 15, "Double '0xf'");
testOk(epicsScanLong("0XF", &l, 0) && l == 15, "Long '0XF'");
testOk(epicsScanULong("0XF", &u, 0) && u == 15, "ULong '0XF'");
testOk(epicsScanFloat("0XF", &f) && f == 15, "Float '0XF'");
testOk(epicsScanDouble("0XF", &d) && d == 15, "Double '0XF'");
testOk(epicsParseLong("0x0", &l, 10) == S_stdlib_extraneous,
"Long '0x0' in base 10 => extraneous");
testOk(epicsParseULong("0x0", &u, 10) == S_stdlib_extraneous,
"ULong '0x0' in base 10 => extraneous");
testOk(epicsScanLong("0x10", &l, 0) && l == 0x10,
"Long '0x10' in base 0");
testOk(epicsScanULong("0x10", &u, 0) && u == 0x10,
"ULong '0x10' in base 0");
testOk(epicsScanLong("0x10", &l, 16) && l == 0x10,
"Long '0x10' in base 16");
testOk(epicsScanULong("0x10", &u, 16) && u == 0x10,
"ULong '0x10' in base 16");
testOk(epicsScanLong("10", &l, 16) && l == 0x10,
"Long '10' in base 16");
testOk(epicsScanULong("10", &u, 16) && u == 0x10,
"ULong '10' in base 16");
testOk(epicsScanLong("0x7fffffff", &l, 0) && l == 0x7fffffff,
"Long '0x7fffffff'");
testOk(epicsScanULong("0xffffffff", &u, 0) && u == 0xffffffff,
"ULong '0xffffffff'");
testOk(epicsScanFloat("0xffffff", &f) && f == 0xffffff,
"Float '0xffffff'");
testOk(epicsScanDouble("0xffffffff", &d) && d == 0xffffffff,
"Double '0xffffffff'");
testOk(epicsScanLong("-0x7fffffff", &l, 0) && l == -0x7fffffff,
"Long '-0x7fffffff'");
testOk(epicsScanULong("-0x7fffffff", &u, 0) && u == -0x7fffffff,
"ULong '-0x7fffffff'");
testOk(epicsScanFloat("-0xffffff", &f) && f == -0xffffff,
"Float '-0xffffff'");
testOk(epicsScanDouble("-0x7fffffff", &d) && d == -0x7fffffff,
"Double '-0x7fffffff'");
testOk(!epicsParseInt8("0x7f", &i8, 0) && i8 == 0x7f,
"Int8 '0x7f'");
testOk(!epicsParseInt8("-0x80", &i8, 0) && i8 == -0x80,
"Int8 '-0x80'");
testOk(!epicsParseUInt8("0xff", &u8, 0) && u8 == 0xff,
"UInt8 '0xff'");
testOk(epicsParseInt8("0x80", &i8, 0) == S_stdlib_overflow,
"Int8 '0x80' => overflow");
testOk(epicsParseInt8("-0x81", &i8, 0) == S_stdlib_overflow,
"Int8 '-0x81' => overflow");
testOk(epicsParseUInt8("0x100", &u8, 0) == S_stdlib_overflow,
"UInt8 '0x100' => overflow");
testOk(!epicsParseInt16("0x7fff", &i16, 0) && i16 == 0x7fff,
"Int16 '0x7fff'");
testOk(!epicsParseInt16("-0x8000", &i16, 0) && i16 == -0x8000,
"Int16 '-0x8000'");
testOk(!epicsParseUInt16("0xffff", &u16, 0) && u16 == 0xffff,
"UInt16 '0xffff'");
testOk(epicsParseInt16("0x8000", &i16, 0) == S_stdlib_overflow,
"Int16 '0x8000' => overflow");
testOk(epicsParseInt16("-0x8001", &i16, 0) == S_stdlib_overflow,
"Int16 '-0x8001' => overflow");
testOk(epicsParseUInt16("0x10000", &u16, 0) == S_stdlib_overflow,
"UInt16 '0x10000' => overflow");
testOk(!epicsParseInt32("0x7fffffff", &i32, 0) && i32 == 0x7fffffff,
"Int32 '0x7fffffff'");
testOk(!epicsParseInt32("-0x80000000", &i32, 0) && i32 == -0x80000000L,
"Int32 '-0x80000000'");
testOk(!epicsParseUInt32("0xffffffff", &u32, 0) && u32 == 0xffffffff,
"UInt32 '0xffffffff'");
testOk(epicsParseInt32("0x80000000", &i32, 0) == S_stdlib_overflow,
"Int32 '0x80000000' => overflow");
testOk(epicsParseInt32("-0x80000001", &i32, 0) == S_stdlib_overflow,
"Int32 '-0x80000001' => overflow");
testOk(epicsParseUInt32("0x100000000", &u32, 0) == S_stdlib_overflow,
"UInt32 '0x100000000' => overflow");
testOk(epicsScanFloat(".1", &f) && fabs(f - 0.1) < 1e-7,
"Float '.1'");
testOk(epicsScanDouble(".1", &d) && fabs(d - 0.1) < 1e-15,
"Double '.1'");
testOk(epicsScanFloat("0.1", &f) && fabs(f - 0.1) < 1e-7,
"Float '0.1'");
testOk(epicsScanDouble("0.1", &d) && fabs(d - 0.1) < 1e-15,
"Double '0.1'");
testOk(epicsScanFloat("1e-1", &f) && fabs(f - 1e-1) < 1e-7,
"Float '1e-1'");
testOk(epicsScanDouble("1e-1", &d) && fabs(d - 1e-1) < 1e-15,
"Double '1e-1'");
testOk(epicsScanFloat("-.1", &f) && fabs(f + 0.1) < 1e-7,
"Float '-.1'");
testOk(epicsScanDouble("-.1", &d) && fabs(d + 0.1) < 1e-15,
"Double '-.1'");
testOk(epicsScanFloat("-0.1", &f) && fabs(f + 0.1) < 1e-7,
"Float '-0.1'");
testOk(epicsScanDouble("-0.1", &d) && fabs(d + 0.1) < 1e-15,
"Double '-0.1'");
testOk(epicsScanFloat("-1e-1", &f) && fabs(f + 1e-1) < 1e-7,
"Float '-1e-1'");
testOk(epicsScanDouble("-1e-1", &d) && fabs(d + 1e-1) < 1e-15,
"Double '-1e-1'");
testOk(epicsScanFloat("1e-30", &f) && fabs(f - 1e-30) < 1e-36,
"Float '1e-30'");
testOk(epicsScanDouble("1e-300", &d) && fabs(d - 1e-300) < 1e-306,
"Double '1e-300'");
testOk(epicsParseFloat("1e-40", &f) == S_stdlib_underflow,
"Float '1e-40' => underflow");
testOk(epicsParseDouble("1e-330", &d) == S_stdlib_underflow,
"Double '1e-330' => underflow");
testOk(epicsScanFloat("1e30", &f) && fabs(f - 1e30) < 1e24,
"Float '1e30'");
testOk(epicsScanDouble("1e300", &d) && d == 1e300,
"Double '1e300'");
testOk(epicsParseFloat("1e40", &f) == S_stdlib_overflow,
"Float '1e40' => overflow");
testOk(epicsParseDouble("1e310", &d) == S_stdlib_overflow,
"Double '1e330' => overflow");
testOk(epicsScanLong("2147483647", &l, 0) && l == 2147483647,
"Long '2147483647'");
testOk(epicsScanLong("-2147483647", &l, 0) && l == -2147483647,
"Long '-2147483647'");
testOk(epicsScanULong("4294967295", &u, 0) && u == 4294967295u,
"ULong '4294967295'");
testOk(epicsScanFloat("16777214", &f) && f == 16777214.0,
"Float '16777214'");
testOk(epicsScanFloat("16777215", &f) && f == 16777215.0,
"Float '16777215'");
testOk(epicsScanFloat("-16777215", &f) && f == -16777215.0,
"Float '-16777215'");
testOk(epicsScanFloat("-16777216", &f) && f == -16777216.0,
"Float '-16777216'");
testOk(epicsScanDouble("4294967294", &d) && d == 4294967294.0,
"Double '4294967294'");
testOk(epicsScanDouble("4294967295", &d) && d == 4294967295.0,
"Double '4294967295'");
testOk(epicsScanDouble("-4294967295", &d) && d == -4294967295.0,
"Double '-4294967295'");
testOk(epicsScanDouble("-4294967296", &d) && d == -4294967296.0,
"Double '-4294967296'");
testOk(epicsScanFloat("NAN", &f) && isnan(f), "Float 'NAN'");
testOk(epicsScanDouble("NAN", &d) && isnan(d), "Double 'NAN'");
testOk(epicsScanFloat("Nan", &f) && isnan(f), "Float 'Nan'");
testOk(epicsScanDouble("Nan", &d) && isnan(d), "Double 'Nan'");
testOk(epicsScanFloat("nan()", &f) && isnan(f), "Float 'nan()'");
testOk(epicsScanDouble("nan()", &d) && isnan(d), "Double 'nan()'");
testOk(epicsScanFloat("INF", &f) && f == epicsINF,
"Float 'INF'");
testOk(epicsScanDouble("INF", &d) && d == epicsINF,
"Double 'INF'");
testOk(epicsScanFloat("Infinity", &f) && f == epicsINF,
"Float 'Infinity'");
testOk(epicsScanDouble("Infinity", &d) && d == epicsINF,
"Double 'Infinity'");
testOk(epicsScanFloat("+INF", &f) && f == epicsINF,
"Float '+INF'");
testOk(epicsScanDouble("+INF", &d) && d == epicsINF,
"Double '+INF'");
testOk(epicsScanFloat("+Infinity", &f) && f == epicsINF,
"Float '+Infinity'");
testOk(epicsScanDouble("+Infinity", &d) && d == epicsINF,
"Double '+Infinity'");
testOk(epicsScanFloat("-INF", &f) && f == -epicsINF,
"Float '-INF'");
testOk(epicsScanDouble("-INF", &d) && d == -epicsINF,
"Double '-INF'");
testOk(epicsScanFloat("-Infinity", &f) && f == -epicsINF,
"Float '-Infinity'");
testOk(epicsScanDouble("-Infinity", &d) && d == -epicsINF,
"Double '-Infinity'");
#ifdef epicsStrtod
#define CHECK_STRTOD epicsStrtod != strtod
if (epicsStrtod == strtod)
testDiag("This target defines epicsStrtod as strtod");
else
testDiag("This target defines its own epicsStrtod macro");
#else
#define CHECK_STRTOD 1
testDiag("This target compiles epicsStrtod()");
#endif
if (CHECK_STRTOD) {
int pass = 0, fail = 0;
#define CHECK(ok, msg) if (ok) pass++; else fail++, testDiag(msg)
testDiag("Checking the native strtod(), only failures shown:");
CHECK(parseStrtod("", &d) == S_stdlib_noConversion,
" not ok - strtod('') => noConversion");
CHECK(parseStrtod("\t \n", &d) == S_stdlib_noConversion,
" not ok - strtod('\\t 1\\n') => noConversion");
CHECK(parseStrtod("!", &d) == S_stdlib_noConversion,
" not ok - strtod('!') => noConversion");
CHECK(scanStrtod("0", &d) && d == 0,
" not ok - strtod('0')");
CHECK(scanStrtod("\t 1\n", &d) && d == 1,
" not ok - strtod('\\t 1\\n')");
CHECK(parseStrtod("2!", &d) == S_stdlib_extraneous,
" not ok - strtod('2!') => extraneous");
CHECK(parseStrtod("3 !", &d) == S_stdlib_extraneous,
" not ok - strtod('3 !') => extraneous");
CHECK(scanStrtod("0x0", &d) && d == 0,
" not ok - strtod('0x0')");
CHECK(scanStrtod("0x1", &d) && d == 1,
" not ok - strtod('0x1')");
CHECK(scanStrtod("+0x1", &d) && d == 1,
" not ok - strtod('+0x1')");
CHECK(scanStrtod("-0x1", &d) && d == -1,
" not ok - strtod('-0x1')");
CHECK(scanStrtod("0xf", &d) && d == 15,
" not ok - strtod('0xf')");
CHECK(scanStrtod("0XF", &d) && d == 15,
" not ok - strtod('0XF')");
CHECK(scanStrtod("0xffffffff", &d) && d == 0xffffffff,
" not ok - strtod('0xffffffff')");
CHECK(scanStrtod("-0x7fffffff", &d) && d == -0x7fffffffl,
" not ok - strtod('-0x7fffffff')");
CHECK(scanStrtod(".1", &d) && fabs(d - 0.1) < 1e-15,
" not ok - strtod('.1')");
CHECK(scanStrtod("0.1", &d) && fabs(d - 0.1) < 1e-15,
" not ok - strtod('0.1')");
CHECK(scanStrtod("1e-1", &d) && fabs(d - 1e-1) < 1e-15,
" not ok - strtod('1e-1')");
CHECK(scanStrtod("-.1", &d) && fabs(d + 0.1) < 1e-15,
" not ok - strtod('-.1')");
CHECK(scanStrtod("-0.1", &d) && fabs(d + 0.1) < 1e-15,
" not ok - strtod('-0.1')");
CHECK(scanStrtod("-1e-1", &d) && fabs(d + 1e-1) < 1e-15,
" not ok - strtod('-1e-1')");
CHECK(scanStrtod("1e-300", &d) && fabs(d - 1e-300) < 1e-306,
" not ok - strtod('1e-300')");
CHECK(parseStrtod("1e-400", &d) == S_stdlib_underflow,
" not ok - strtod('1e-400') => underflow");
CHECK(scanStrtod("4294967294", &d) && d == 4294967294.0,
" not ok - strtod('4294967294')");
CHECK(scanStrtod("4294967295", &d) && d == 4294967295.0,
" not ok - strtod('4294967295')");
CHECK(scanStrtod("-4294967295", &d) && d == -4294967295.0,
" not ok - strtod('-4294967295')");
CHECK(scanStrtod("-4294967296", &d) && d == -4294967296.0,
" not ok - strtod('-4294967296')");
CHECK(scanStrtod("NAN", &d) && isnan(d),
" not ok - strtod('NAN')");
CHECK(scanStrtod("Nan", &d) && isnan(d),
" not ok - strtod('Nan')");
CHECK(scanStrtod("nan()", &d) && isnan(d),
" not ok - strtod('nan()')");
CHECK(scanStrtod("INF", &d) && d == epicsINF,
" not ok - strtod('INF')");
CHECK(scanStrtod("Infinity", &d) && d == epicsINF,
" not ok - strtod('Infinity')");
CHECK(scanStrtod("+INF", &d) && d == epicsINF,
" not ok - strtod('+INF')");
CHECK(scanStrtod("+Infinity", &d) && d == epicsINF,
" not ok - strtod('+Infinity')");
CHECK(scanStrtod("-INF", &d) && d == -epicsINF,
" not ok - strtod('-INF')");
CHECK(scanStrtod("-Infinity", &d) && d == -epicsINF,
" not ok - strtod('-Infinity')");
if (fail)
testDiag("The native strtod() passed %d and failed %d tests",
pass, fail);
else {
testDiag("The native strtod() passed all %d tests", pass);
#ifndef epicsStrtod
testDiag("The compiled epicsStrtod() should not be needed");
#endif
}
}
return testDone();
}