ENH: handle underflow (rounding) of float/double as zero (issue #625)
- The problem occurs when using atof to parse values such as "1e-39" since this is out of range for a float and _can_ set errno to ERANGE. Similar to parsing of integers, now parse with the longest floating point representation "long double" via strtold (guaranteed to be part of C++11) and verify against the respective VGREAT values for overflow. Treat anything smaller than VSMALL to be zero.
This commit is contained in:
parent
11da9bdc36
commit
c1c4e91ffb
@ -118,7 +118,8 @@ int main(int argc, char *argv[])
|
|||||||
unsigned nFail = 0;
|
unsigned nFail = 0;
|
||||||
|
|
||||||
{
|
{
|
||||||
Info<< nl << "Test readDouble:" << nl;
|
Info<< nl << "Test readDouble: (small=" << doubleScalarVSMALL
|
||||||
|
<< " great=" << doubleScalarVSMALL << "):" << nl;
|
||||||
nFail += testParsing
|
nFail += testParsing
|
||||||
(
|
(
|
||||||
&readDouble,
|
&readDouble,
|
||||||
@ -131,12 +132,18 @@ int main(int argc, char *argv[])
|
|||||||
{ " 3.14159 ", true },
|
{ " 3.14159 ", true },
|
||||||
{ " 31.4159E-1 " , true },
|
{ " 31.4159E-1 " , true },
|
||||||
{ " 100E1000 " , false },
|
{ " 100E1000 " , false },
|
||||||
|
{ " 1E-40 " , true },
|
||||||
|
{ " 1E-305 " , true },
|
||||||
|
{ " 1E-37 " , true },
|
||||||
|
{ " 1E-300 " , true },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Info<< nl << "Test readFloat:" << nl;
|
Info<< nl << "Test readFloat: (small=" << floatScalarVSMALL
|
||||||
|
<< " great=" << floatScalarVGREAT << "):" << nl;
|
||||||
|
|
||||||
nFail += testParsing
|
nFail += testParsing
|
||||||
(
|
(
|
||||||
&readFloat,
|
&readFloat,
|
||||||
@ -145,6 +152,10 @@ int main(int argc, char *argv[])
|
|||||||
{ " 31.4159E-1 " , true },
|
{ " 31.4159E-1 " , true },
|
||||||
{ " 31.4159E200 " , false },
|
{ " 31.4159E200 " , false },
|
||||||
{ " 31.4159E20 " , true },
|
{ " 31.4159E20 " , true },
|
||||||
|
{ " 1E-40 " , true },
|
||||||
|
{ " 1E-305 " , true },
|
||||||
|
{ " 1E-37 " , true },
|
||||||
|
{ " 1E-300 " , true },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -160,12 +171,16 @@ int main(int argc, char *argv[])
|
|||||||
{ " 314.159-2 " , true },
|
{ " 314.159-2 " , true },
|
||||||
{ " 31.4159E200 " , true },
|
{ " 31.4159E200 " , true },
|
||||||
{ " 31.4159E20 " , true },
|
{ " 31.4159E20 " , true },
|
||||||
|
{ " 1E-40 " , true },
|
||||||
|
{ " 1E-305 " , true },
|
||||||
|
{ " 1E-37 " , true },
|
||||||
|
{ " 1E-300 " , true },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Info<< nl << "Test readInt32 (max= " << INT32_MAX << "):" << nl;
|
Info<< nl << "Test readInt32 (max=" << INT32_MAX << "):" << nl;
|
||||||
nFail += testParsing
|
nFail += testParsing
|
||||||
(
|
(
|
||||||
&readInt32,
|
&readInt32,
|
||||||
@ -181,7 +196,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Info<< nl << "Test readUint32 (max= "
|
Info<< nl << "Test readUint32 (max="
|
||||||
<< unsigned(UINT32_MAX) << "):" << nl;
|
<< unsigned(UINT32_MAX) << "):" << nl;
|
||||||
nFail += testParsing
|
nFail += testParsing
|
||||||
(
|
(
|
||||||
|
@ -80,10 +80,15 @@ Scalar ScalarRead(const char* buf)
|
|||||||
{
|
{
|
||||||
char* endptr = nullptr;
|
char* endptr = nullptr;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
const auto parsed = ScalarConvert(buf, &endptr);
|
||||||
|
|
||||||
const Scalar val = ScalarConvert(buf, &endptr);
|
const parsing::errorType err =
|
||||||
|
(
|
||||||
|
(parsed < -ScalarVGREAT || parsed > ScalarVGREAT)
|
||||||
|
? parsing::errorType::RANGE
|
||||||
|
: parsing::checkConversion(buf, endptr)
|
||||||
|
);
|
||||||
|
|
||||||
const parsing::errorType err = parsing::checkConversion(buf, endptr);
|
|
||||||
if (err != parsing::errorType::NONE)
|
if (err != parsing::errorType::NONE)
|
||||||
{
|
{
|
||||||
FatalIOErrorInFunction("unknown")
|
FatalIOErrorInFunction("unknown")
|
||||||
@ -91,7 +96,13 @@ Scalar ScalarRead(const char* buf)
|
|||||||
<< exit(FatalIOError);
|
<< exit(FatalIOError);
|
||||||
}
|
}
|
||||||
|
|
||||||
return val;
|
// Round underflow to zero
|
||||||
|
return
|
||||||
|
(
|
||||||
|
(parsed > -ScalarVSMALL && parsed < ScalarVSMALL)
|
||||||
|
? 0
|
||||||
|
: Scalar(parsed)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -99,10 +110,22 @@ bool readScalar(const char* buf, Scalar& val)
|
|||||||
{
|
{
|
||||||
char* endptr = nullptr;
|
char* endptr = nullptr;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
const auto parsed = ScalarConvert(buf, &endptr);
|
||||||
|
|
||||||
val = ScalarConvert(buf, &endptr);
|
// Round underflow to zero
|
||||||
|
val =
|
||||||
|
(
|
||||||
|
(parsed > -ScalarVSMALL && parsed < ScalarVSMALL)
|
||||||
|
? 0
|
||||||
|
: Scalar(parsed)
|
||||||
|
);
|
||||||
|
|
||||||
return (parsing::checkConversion(buf, endptr) == parsing::errorType::NONE);
|
return
|
||||||
|
(
|
||||||
|
(parsed < -ScalarVGREAT || parsed > ScalarVGREAT)
|
||||||
|
? false
|
||||||
|
: (parsing::checkConversion(buf, endptr) == parsing::errorType::NONE)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ License
|
|||||||
#include "parsing.H"
|
#include "parsing.H"
|
||||||
#include "IOstreams.H"
|
#include "IOstreams.H"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
@ -40,7 +41,8 @@ License
|
|||||||
#define ScalarROOTVGREAT doubleScalarROOTVGREAT
|
#define ScalarROOTVGREAT doubleScalarROOTVGREAT
|
||||||
#define ScalarROOTVSMALL doubleScalarROOTVSMALL
|
#define ScalarROOTVSMALL doubleScalarROOTVSMALL
|
||||||
#define ScalarRead readDouble
|
#define ScalarRead readDouble
|
||||||
#define ScalarConvert ::strtod
|
// Convert using larger representation to properly capture underflow
|
||||||
|
#define ScalarConvert ::strtold
|
||||||
|
|
||||||
#include "Scalar.C"
|
#include "Scalar.C"
|
||||||
|
|
||||||
|
@ -40,7 +40,8 @@ License
|
|||||||
#define ScalarROOTVGREAT floatScalarROOTVGREAT
|
#define ScalarROOTVGREAT floatScalarROOTVGREAT
|
||||||
#define ScalarROOTVSMALL floatScalarROOTVSMALL
|
#define ScalarROOTVSMALL floatScalarROOTVSMALL
|
||||||
#define ScalarRead readFloat
|
#define ScalarRead readFloat
|
||||||
#define ScalarConvert ::strtof
|
// Convert using larger representation to properly capture underflow
|
||||||
|
#define ScalarConvert ::strtod
|
||||||
|
|
||||||
#include "Scalar.C"
|
#include "Scalar.C"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user