From d7eada7216b7bff9316dfb9ba4b0ddae9364891a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 15 Apr 2013 14:39:42 -0400 Subject: [PATCH] type casting with castUnsafe(FROM val) --- pvDataApp/Makefile | 2 + pvDataApp/misc/parseToPOD.cpp | 373 +++++++++++++++++++++++++++++++++ pvDataApp/misc/typeCast.h | 177 ++++++++++++++++ testApp/misc/Makefile | 5 + testApp/misc/testTypeCast.cpp | 376 ++++++++++++++++++++++++++++++++++ 5 files changed, 933 insertions(+) create mode 100644 pvDataApp/misc/parseToPOD.cpp create mode 100644 pvDataApp/misc/typeCast.h create mode 100644 testApp/misc/testTypeCast.cpp diff --git a/pvDataApp/Makefile b/pvDataApp/Makefile index 0acf9b9..c496310 100644 --- a/pvDataApp/Makefile +++ b/pvDataApp/Makefile @@ -24,6 +24,7 @@ INC += destroyable.h INC += status.h INC += sharedPtr.h INC += localStaticLock.h +INC += typeCast.h LIBSRCS += byteBuffer.cpp LIBSRCS += bitSet.cpp @@ -37,6 +38,7 @@ LIBSRCS += timer.cpp LIBSRCS += status.cpp LIBSRCS += messageQueue.cpp LIBSRCS += localStaticLock.cpp +LIBSRCS += parseToPOD.cpp SRC_DIRS += $(PVDATA)/pv diff --git a/pvDataApp/misc/parseToPOD.cpp b/pvDataApp/misc/parseToPOD.cpp new file mode 100644 index 0000000..a047348 --- /dev/null +++ b/pvDataApp/misc/parseToPOD.cpp @@ -0,0 +1,373 @@ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "typeCast.h" + +#ifndef EPICS_VERSION_INT +#define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) +#define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL) +#endif + +#if EPICS_VERSION_INT < VERSION_INT(3,15,0,1) +/* integer conversion primatives added to epicsStdlib.c in 3.15.0.1 */ + +#define S_stdlib_noConversion 1 /* No digits to convert */ +#define S_stdlib_extraneous 2 /* Extraneous characters */ +#define S_stdlib_underflow 3 /* Too small to represent */ +#define S_stdlib_overflow 4 /* Too large to represent */ +#define S_stdlib_badBase 5 /* Number base not supported */ + +static int +epicsParseLong(const char *str, long *to, int base, char **units) +{ + int c; + char *endp; + long value; + + while ((c = *str) && isspace(c)) + ++str; + + errno = 0; + value = strtol(str, &endp, base); + + if (endp == str) + return S_stdlib_noConversion; + if (errno == EINVAL) /* Not universally supported */ + return S_stdlib_badBase; + if (errno == ERANGE) + return S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} + +static int +epicsParseULong(const char *str, unsigned long *to, int base, char **units) +{ + int c; + char *endp; + unsigned long value; + + while ((c = *str) && isspace(c)) + ++str; + + errno = 0; + value = strtoul(str, &endp, base); + + if (endp == str) + return S_stdlib_noConversion; + if (errno == EINVAL) /* Not universally supported */ + return S_stdlib_badBase; + if (errno == ERANGE) + return S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} + +static int +epicsParseDouble(const char *str, double *to, char **units) +{ + 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; + if (errno == ERANGE) + return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} + + +/* These call the primitives */ + +static int +epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units) +{ + long value; + int status = epicsParseLong(str, &value, base, units); + + if (status) + return status; + + if (value < -0x80 || value > 0x7f) + return S_stdlib_overflow; + + *to = value; + return 0; +} + +static int +epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units) +{ + unsigned long value; + int status = epicsParseULong(str, &value, base, units); + + if (status) + return status; + + if (value > 0xff && value <= ~0xffUL) + return S_stdlib_overflow; + + *to = value; + return 0; +} + +static int +epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units) +{ + long value; + int status = epicsParseLong(str, &value, base, units); + + if (status) + return status; + + if (value < -0x8000 || value > 0x7fff) + return S_stdlib_overflow; + + *to = value; + return 0; +} + +static int +epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units) +{ + unsigned long value; + int status = epicsParseULong(str, &value, base, units); + + if (status) + return status; + + if (value > 0xffff && value <= ~0xffffUL) + return S_stdlib_overflow; + + *to = value; + return 0; +} + +static int +epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units) +{ + long value; + int status = epicsParseLong(str, &value, base, units); + + if (status) + return status; + +#if (LONG_MAX > 0x7fffffff) + if (value < -0x80000000L || value > 0x7fffffffL) + return S_stdlib_overflow; +#endif + + *to = value; + return 0; +} + +static int +epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units) +{ + unsigned long value; + int status = epicsParseULong(str, &value, base, units); + + if (status) + return status; + +#if (ULONG_MAX > 0xffffffff) + if (value > 0xffffffffUL && value <= ~0xffffffffUL) + return S_stdlib_overflow; +#endif + + *to = value; + return 0; +} + +static int +epicsParseFloat(const char *str, float *to, char **units) +{ + double value, abs; + int status = epicsParseDouble(str, &value, units); + + 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; +} +#endif + +/* do we need long long? */ +#if INT_MAX == LONG_MAX +static int +epicsParseLongLong(const char *str, long long *to, int base, char **units) +{ + int c; + char *endp; + long long value; + + while ((c = *str) && isspace(c)) + ++str; + + errno = 0; + value = strtoll(str, &endp, base); + + if (endp == str) + return S_stdlib_noConversion; + if (errno == EINVAL) /* Not universally supported */ + return S_stdlib_badBase; + if (errno == ERANGE) + return S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} + +static int +epicsParseULongLong(const char *str, unsigned long long *to, int base, char **units) +{ + int c; + char *endp; + unsigned long long value; + + while ((c = *str) && isspace(c)) + ++str; + + errno = 0; + value = strtoull(str, &endp, base); + + if (endp == str) + return S_stdlib_noConversion; + if (errno == EINVAL) /* Not universally supported */ + return S_stdlib_badBase; + if (errno == ERANGE) + return S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} +#endif + +static +void handleParseError(int err) +{ + switch(err) { + case 0: break; + case S_stdlib_noConversion: throw std::runtime_error("parseToPOD: No digits to convert"); + case S_stdlib_extraneous: throw std::runtime_error("parseToPOD: Extraneous characters"); + case S_stdlib_underflow: throw std::runtime_error("parseToPOD: Too small to represent"); + case S_stdlib_overflow: throw std::runtime_error("parseToPOD: Too large to represent"); + case S_stdlib_badBase: throw std::runtime_error("parseToPOD: Number base not supported"); + default: + throw std::runtime_error("parseToPOD: unknown error"); + } +} + +namespace epics { namespace pvData { namespace detail { + +void parseToPOD(const std::string& in, int8 *out) { + epicsInt8 temp; + int err = epicsParseInt8(in.c_str(), &temp, 0, NULL); + if(err) handleParseError(err); + else *out = temp; +} + +#define INTFN(T, S) \ +void parseToPOD(const std::string& in, T *out) { \ + int err = epicsParse ## S(in.c_str(), out, 0, NULL); \ + if(err) handleParseError(err); \ +} + +INTFN(char, Int8); +INTFN(uint8_t, UInt8); +INTFN(int16_t, Int16); +INTFN(uint16_t, UInt16); +INTFN(int32_t, Int32); +INTFN(uint32_t, UInt32); + +void parseToPOD(const std::string& in, int64_t *out) { +#if INT_MAX == LONG_MAX + int err = epicsParseLongLong(in.c_str(), out, 0, NULL); +#else + int err = epicsParseLong(in.c_str(), out, 0, NULL); +#endif + if(err) handleParseError(err); +} + +void parseToPOD(const std::string& in, uint64_t *out) { +#if INT_MAX == LONG_MAX + int err = epicsParseULongLong(in.c_str(), out, 0, NULL); +#else + int err = epicsParseULong(in.c_str(), out, 0, NULL); +#endif + if(err) handleParseError(err); +} + +void parseToPOD(const std::string& in, float *out) { + int err = epicsParseFloat(in.c_str(), out, NULL); + if(err) handleParseError(err); +} + +void parseToPOD(const std::string& in, double *out) { + int err = epicsParseDouble(in.c_str(), out, NULL); + if(err) handleParseError(err); +} + +}}} diff --git a/pvDataApp/misc/typeCast.h b/pvDataApp/misc/typeCast.h new file mode 100644 index 0000000..dbf8b1a --- /dev/null +++ b/pvDataApp/misc/typeCast.h @@ -0,0 +1,177 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvData is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/* Author: Michael Davidsaver */ +#ifndef PVTYPECAST_H +#define PVTYPECAST_H + +#include +#include + +#include + +#include + +// gently nudge the compiler to inline our wrappers +#if defined(__GNUC__) +# define FORCE_INLINE __attribute__((always_inline)) inline +#elif defined(_MSC_VER) +# define FORCE_INLINE __forceinline +#else +# define FORCE_INLINE inline +#endif + +namespace epics { namespace pvData { + +typedef std::string String; + +namespace detail { + // parseToPOD wraps the epicsParse*() functions in one name + // and throws exceptions + void parseToPOD(const std::string&, char *out); + void parseToPOD(const std::string&, int8_t *out); + void parseToPOD(const std::string&, uint8_t *out); + void parseToPOD(const std::string&, int16_t *out); + void parseToPOD(const std::string&, uint16_t *out); + void parseToPOD(const std::string&, int32_t *out); + void parseToPOD(const std::string&, uint32_t *out); + void parseToPOD(const std::string&, int64_t *out); + void parseToPOD(const std::string&, uint64_t *out); + void parseToPOD(const std::string&, float *out); + void parseToPOD(const std::string&, double *out); + + /* want to pass POD types by value, + * and String by const reference + */ + template + struct cast_arg { typedef ARG arg; }; + template<> + struct cast_arg { typedef const String& arg; }; + + // test to allow specialization only when A!=B + template + struct not_same_type {typedef R type;}; + template + struct not_same_type {}; + + // trick std::ostream into treating char's as numbers + // by promoting char to int + template + struct print_cast { typedef T type; }; + template<> + struct print_cast { typedef int type; }; + template<> + struct print_cast { typedef signed int type; }; + template<> + struct print_cast { typedef unsigned int type; }; + + // default to C++ type casting + template + struct cast_helper { + static FORCE_INLINE TO op(FROM from) { + return static_cast(from); + } + }; + + // special handling when down-casting double to float + template<> + struct cast_helper { + static FORCE_INLINE float op(double from) { + return epicsConvertDoubleToFloat(from); + } + }; + + // print POD to string + // when String!=FROM + template + struct cast_helper::type> { + static String op(FROM from) { + typedef typename print_cast::type ptype; + std::ostringstream strm; + strm << (ptype)from; + if(strm.fail()) + throw std::runtime_error("Cast to string failed"); + return strm.str(); + } + }; + + // parse POD from string + // TO!=String + template + struct cast_helper::type> { + static FORCE_INLINE TO op(const String& from) { + TO ret; + parseToPOD(from, &ret); + return ret; + } + }; + +} // end detail + +/** @brief Casting/converting between supported scalar types. + * + * Supported types: uint8_t, int8_t, uint16_t, int16_t, + * uint32_t, int32_t, uint64_t, int64_t, + * float, double, String + * + * As defined in pvType.h + * + @throws std::runtime_error when the cast is not possible. + @throws std::bad_alloc when the cast is not possible. + * + @section convertg Conversion Guarantees + * + * Conversions which always produce a correct result. + * + * - signed integer -> larger signed integer + * - unsigned integer -> larger unsigned integer + * - integer -> float or double (where sizeof(integer) double + * + * Conversions where out of range inputs always produce + * a defined result, but may not be reversable. + * + * - double -> float. When abs(value) is outside the range + * [FLT_MIN, FLT_MAX] the value is clipped to FLT_MIN or FLT_MAX + * with the sign preserved. + * + * Conversions where invalid or out of range inputs result + * in an exception. + * + * - non-String -> String + * - String -> non-String + * - String -> String (throws only std::bad_alloc) + * + * Conversions where out of range inputs produce undefined + * results. + * + * - signed integer -> smaller signed integer + * - unsigned integer -> smaller unsigned integer + * - signed integer <-> unsigned integer + * - integer -> float or double (where sizeof(integer)>=sizeof(floating)) + * - float or double -> integer. The floating point value + * is rounded towards zero. However, the result for values + * too large to be represented by the integer type + * is not defined. + * + @section stringf String formats + * + * - Numbers beginning with 1-9 are parsed as base-10. + * - Numbers beginning with '0x' are parsed as base-16 + * - Numbers beginning with '0' are parsed as base-8. + * - Hex numbers are case insensitive. + * - Exponential numbers may use either 'e' or 'E'. + */ +template +static FORCE_INLINE TO castUnsafe(const FROM& from) +{ + return detail::cast_helper::op(from); +} + +}} // end namespace + +#undef FORCE_INLINE + +#endif // PVTYPECAST_H diff --git a/testApp/misc/Makefile b/testApp/misc/Makefile index 077f4eb..4e2cf83 100644 --- a/testApp/misc/Makefile +++ b/testApp/misc/Makefile @@ -42,6 +42,11 @@ PROD_HOST += testMessageQueue testMessageQueue_SRCS += testMessageQueue.cpp testMessageQueue_LIBS += pvData Com +PROD_HOST += testTypeCast +testTypeCast_SRCS += testTypeCast.cpp +testTypeCast_LIBS += pvData Com + + include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE diff --git a/testApp/misc/testTypeCast.cpp b/testApp/misc/testTypeCast.cpp new file mode 100644 index 0000000..239bf70 --- /dev/null +++ b/testApp/misc/testTypeCast.cpp @@ -0,0 +1,376 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvData is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/* Author: Michael Davidsaver */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pv/typeCast.h" + +#include + +using epics::pvData::String; + +namespace { + + template + struct testequal { + static bool op(T A, T B) {return A==B; } + }; + template<> + struct testequal { + static bool op(double A, double B) {return fabs(A-B)<1e-300; } + }; + template<> + struct testequal { + static bool op(float A, float B) {return fabs(A-B)<1e-30; } + }; + + template + struct testcase { + static bool op(std::ostream& msg, TO expect, FROM inp) + { + TO actual; + try { + actual = ::epics::pvData::castUnsafe(inp); + //actual = ::epics::pvData::detail::cast_helper::op(inp); + } catch(std::runtime_error& e) { + msg<<"Failed to cast " + < " + <::op(actual, expect)) { + msg<<"Failed cast gives unexpected value " + < " + < " + < + struct testfail { + static bool op(std::ostream& msg, FROM inp) + { + TO actual; + try { + actual = ::epics::pvData::castUnsafe(inp); + msg<<"Failed to generate expected error " + < (" + < (" + <::op(*out, VTO, VFRO) + +// Test cast and reverse +#define TEST2(TTO, VTO, TFRO, VFRO) TEST(TTO, VTO, TFRO, VFRO); TEST(TFRO, VFRO, TTO, VTO) + +#define FAIL(TTO, TFRO, VFRO) fail |= !testfail::op(*out, VFRO) + +} // end namespace + + +int main(int argc,char *argv[]) +{ + std::ostream *out = &std::cerr; + std::ofstream outf; + if(argc>1){ + outf.open(argv[1]); + if(!outf.good()) { + std::cerr<<"Failed to open "<(x## N) +//#define CHECK(M, N) x## M = ::epics::pvData::detail::cast_helper::op(x## N) + CHECK(int8, int8); + CHECK(int8, uint8); + CHECK(int8, int16); + CHECK(int8, uint16); + CHECK(int8, int32); + CHECK(int8, uint32); + CHECK(int8, int64); + CHECK(int8, uint64); + CHECK(int8, float); + CHECK(int8, double); + CHECK(int8, String); + + CHECK(uint8, int8); + CHECK(uint8, uint8); + CHECK(uint8, int16); + CHECK(uint8, uint16); + CHECK(uint8, int32); + CHECK(uint8, uint32); + CHECK(uint8, int64); + CHECK(uint8, uint64); + CHECK(uint8, float); + CHECK(uint8, double); + CHECK(uint8, String); + + CHECK(int16, int8); + CHECK(int16, uint8); + CHECK(int16, int16); + CHECK(int16, uint16); + CHECK(int16, int32); + CHECK(int16, uint32); + CHECK(int16, int64); + CHECK(int16, uint64); + CHECK(int16, float); + CHECK(int16, double); + CHECK(int16, String); + + CHECK(uint16, int8); + CHECK(uint16, uint8); + CHECK(uint16, int16); + CHECK(uint16, uint16); + CHECK(uint16, int32); + CHECK(uint16, uint32); + CHECK(uint16, int64); + CHECK(uint16, uint64); + CHECK(uint16, float); + CHECK(uint16, double); + CHECK(uint16, String); + + CHECK(int32, int8); + CHECK(int32, uint8); + CHECK(int32, int16); + CHECK(int32, uint16); + CHECK(int32, int32); + CHECK(int32, uint32); + CHECK(int32, int64); + CHECK(int32, uint64); + CHECK(int32, float); + CHECK(int32, double); + CHECK(int32, String); + + CHECK(uint32, int8); + CHECK(uint32, uint8); + CHECK(uint32, int16); + CHECK(uint32, uint16); + CHECK(uint32, int32); + CHECK(uint32, uint32); + CHECK(uint32, int64); + CHECK(uint32, uint64); + CHECK(uint32, float); + CHECK(uint32, double); + CHECK(uint32, String); + + CHECK(int64, int8); + CHECK(int64, uint8); + CHECK(int64, int16); + CHECK(int64, uint16); + CHECK(int64, int32); + CHECK(int64, uint32); + CHECK(int64, int64); + CHECK(int64, uint64); + CHECK(int64, float); + CHECK(int64, double); + //CHECK(int64, String); + + CHECK(uint64, int8); + CHECK(uint64, uint8); + CHECK(uint64, int16); + CHECK(uint64, uint16); + CHECK(uint64, int32); + CHECK(uint64, uint32); + CHECK(uint64, int64); + CHECK(uint64, uint64); + CHECK(uint64, float); + CHECK(uint64, double); + //CHECK(uint64, String); + + CHECK(float, int8); + CHECK(float, uint8); + CHECK(float, int16); + CHECK(float, uint16); + CHECK(float, int32); + CHECK(float, uint32); + CHECK(float, int64); + CHECK(float, uint64); + CHECK(float, float); + CHECK(float, double); + CHECK(float, String); + + CHECK(double, int8); + CHECK(double, uint8); + CHECK(double, int16); + CHECK(double, uint16); + CHECK(double, int32); + CHECK(double, uint32); + CHECK(double, int64); + CHECK(double, uint64); + CHECK(double, float); + CHECK(double, double); + CHECK(double, String); + + CHECK(String, int8); + CHECK(String, uint8); + CHECK(String, int16); + CHECK(String, uint16); + CHECK(String, int32); + CHECK(String, uint32); + CHECK(String, int64); + CHECK(String, uint64); + CHECK(String, float); + CHECK(String, double); + CHECK(String, String); +#undef CHECK + + *out << "Integer signed <=> unsigned\n"; + + TEST2(uint8_t, std::numeric_limits::max(), int8_t, -1); + TEST2(uint16_t, std::numeric_limits::max(), int8_t, -1); + TEST2(uint32_t, std::numeric_limits::max(), int8_t, -1); + TEST2(uint64_t, std::numeric_limits::max(), int8_t, -1); + + *out << "Integer unsigned promote (and demote when in range)\n"; + + TEST2(uint16_t, 0xff, uint8_t, 0xff); + TEST2(uint32_t, 0xffff, uint16_t, 0xffff); + TEST2(uint64_t, 0xffffffffu, uint32_t, 0xffffffffu); + + TEST2(int16_t, 0x7f, int8_t, 0x7f); + TEST2(int32_t, 0x7fff, int16_t, 0x7fff); + TEST2(int64_t, 0x7fffffff, int32_t, 0x7fffffff); + + *out << "Double to int w/ round towards zero (aka truncation)\n"; + + TEST(int32_t, 2, double, 2.1); + TEST(int32_t, 2, double, 2.5); + TEST(int32_t, 2, double, 2.7); + TEST(int32_t, -2, double, -2.1); + TEST(int32_t, -2, double, -2.5); + TEST(int32_t, -2, double, -2.7); + + *out << "Float to int w/ round towards zero (aka truncation)\n"; + + TEST(int32_t, 2, float, 2.1); + TEST(int32_t, 2, float, 2.5); + TEST(int32_t, 2, float, 2.7); + TEST(int32_t, -2, float, -2.1); + TEST(int32_t, -2, float, -2.5); + TEST(int32_t, -2, float, -2.7); + + *out << "String Printing/parsing\n"; + + TEST2(String, "1", int32_t, 1); + TEST2(String, "-1", int32_t, -1); + TEST2(String, "1", int8_t, 1); + TEST2(String, "-1", int8_t, -1); + TEST2(String, "1", uint8_t, 1); + TEST2(String, "-1", char, -1); + + TEST2(String, "127", int32_t, std::numeric_limits::max()); + TEST2(String, "-128", int32_t, std::numeric_limits::min()); + TEST2(String, "255", int32_t, std::numeric_limits::max()); + + TEST2(String, "32767", int32_t, std::numeric_limits::max()); + TEST2(String, "-32768", int32_t, std::numeric_limits::min()); + TEST2(String, "65535", int32_t, std::numeric_limits::max()); + + TEST2(String, "2147483647", int32_t, std::numeric_limits::max()); + TEST2(String, "-2147483648", int32_t, std::numeric_limits::min()); + TEST2(String, "4294967295", uint32_t, std::numeric_limits::max()); + + TEST2(String, "9223372036854775807", int64_t, std::numeric_limits::max()); + TEST2(String, "-9223372036854775808", int64_t, std::numeric_limits::min()); + TEST2(String, "18446744073709551615", uint64_t, std::numeric_limits::max()); + + TEST2(String, "1.1", double, 1.1); + TEST2(String, "1.1e+100", double, 1.1e100); + TEST2(String, "1.1e-100", double, 1.1e-100); + + TEST(double, 1.1e100, String, "1.1E+100"); + + *out << "String Parsing\n"; + + TEST(int32_t, 15, String, "0xf"); + TEST(int32_t, 15, String, "0xF"); + TEST(int32_t, -15, String, "-0xF"); + TEST(int32_t, 16, String, "0x10"); + TEST(int32_t, -16, String, "-0x10"); + + TEST(int32_t, 7, String, "07"); + TEST(int32_t, 8, String, "010"); + TEST(int32_t, -7, String, "-07"); + TEST(int32_t, -8, String, "-010"); + + *out << "String parsing errors\n"; + + FAIL(int32_t, String, "hello!"); + FAIL(int32_t, String, "42 is the answer"); + FAIL(double, String, "hello!"); + FAIL(double, String, "42 is the answer"); + + FAIL(int8_t, String, "1000"); + FAIL(int8_t, String, "-1000"); + + FAIL(double, String, "1e+10000000"); + + *out << "Floating point overflows\n"; + + TEST(float, FLT_MAX, double, 1e300); + TEST(float, -FLT_MAX, double, -1e300); + TEST(float, FLT_MIN, double, 1e-300); + TEST(float, -FLT_MIN, double, -1e-300); + xfloat = ::epics::pvData::castUnsafe(epicsNAN); + *out << "Cast double NAN to float NAN yields: "<