PStringUtils::ToDouble: signal conversion errors like ToInt
ToDouble() had the same shortcoming as ToInt(): wrapping strtod() with a nullptr endptr made a failed conversion indistinguishable from a legitimate 0.0. Add an optional `bool *ok` out-parameter that reports success. strtod() (with endptr + errno) is kept instead of std::from_chars so the accepted input set stays identical to TString::Atof() (leading whitespace skipped, leading '+' honoured, trailing characters ignored); ok is set false on a non-numeric string or an ERANGE overflow. The parameter defaults to nullptr, so existing call sites keep compiling unchanged. Convert the IsFloat-guarded ToDouble call sites in PMsrHandler to the single-parse ToDouble(token, &ok) form (replacing the IsFloat() guard + separate ToDouble() that parsed every token twice). All downstream >=0 / <=0 / range checks are preserved, and push_back sites only append on success so no spurious 0.0 is stored on error. Number-vs-keyword discriminators (pos.error/boundary "none", rrf_phase/fourier-phase parX) are restructured so the keyword branch is taken when ok is false. As a side effect this fixes a latent gap in the GLOBAL rrf_freq handler, where a non-numeric frequency previously slipped through with a stale value instead of raising an error. The IsFloat-guarded ToInt fit-range offsets (fgb/lgb) are intentionally left untouched, as there the guard type differs from the conversion. All 85 integration tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+123
-120
@@ -2874,15 +2874,13 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines)
|
||||
param.fName = tokens[1].c_str();
|
||||
|
||||
// parameter value
|
||||
if (PStringUtils::IsFloat(tokens[2]))
|
||||
param.fValue = PStringUtils::ToDouble(tokens[2]);
|
||||
else
|
||||
param.fValue = PStringUtils::ToDouble(tokens[2], &ok);
|
||||
if (!ok)
|
||||
error = true;
|
||||
|
||||
// parameter value
|
||||
if (PStringUtils::IsFloat(tokens[3]))
|
||||
param.fStep = PStringUtils::ToDouble(tokens[3]);
|
||||
else
|
||||
// parameter step
|
||||
param.fStep = PStringUtils::ToDouble(tokens[3], &ok);
|
||||
if (!ok)
|
||||
error = true;
|
||||
|
||||
// 4 values, i.e. No Name Value Step
|
||||
@@ -2895,14 +2893,13 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines)
|
||||
param.fNoOfParams = 5;
|
||||
|
||||
// positive error
|
||||
if (PStringUtils::IsFloat(tokens[4])) {
|
||||
param.fPosError = PStringUtils::ToDouble(tokens[4], &ok);
|
||||
if (ok) {
|
||||
param.fPosErrorPresent = true;
|
||||
param.fPosError = PStringUtils::ToDouble(tokens[4]);
|
||||
} else if (PStringUtils::IsEqualNoCase(tokens[4], "none")) {
|
||||
param.fPosErrorPresent = false;
|
||||
} else {
|
||||
if (PStringUtils::IsEqualNoCase(tokens[4], "none"))
|
||||
param.fPosErrorPresent = false;
|
||||
else
|
||||
error = true;
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2911,14 +2908,13 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines)
|
||||
param.fNoOfParams = 7;
|
||||
|
||||
// positive error
|
||||
if (PStringUtils::IsFloat(tokens[4])) {
|
||||
param.fPosError = PStringUtils::ToDouble(tokens[4], &ok);
|
||||
if (ok) {
|
||||
param.fPosErrorPresent = true;
|
||||
param.fPosError = PStringUtils::ToDouble(tokens[4]);
|
||||
} else if (PStringUtils::IsEqualNoCase(tokens[4], "none")) {
|
||||
param.fPosErrorPresent = false;
|
||||
} else {
|
||||
if (PStringUtils::IsEqualNoCase(tokens[4], "none"))
|
||||
param.fPosErrorPresent = false;
|
||||
else
|
||||
error = true;
|
||||
error = true;
|
||||
}
|
||||
|
||||
// lower boundary
|
||||
@@ -2926,8 +2922,8 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines)
|
||||
if (PStringUtils::IsEqualNoCase(tokens[5], "none")) { // none
|
||||
param.fLowerBoundaryPresent = false;
|
||||
} else { // assuming that the lower boundary is a number
|
||||
if (PStringUtils::IsFloat(tokens[5])) {
|
||||
param.fLowerBoundary = PStringUtils::ToDouble(tokens[5]);
|
||||
param.fLowerBoundary = PStringUtils::ToDouble(tokens[5], &ok);
|
||||
if (ok) {
|
||||
param.fLowerBoundaryPresent = true;
|
||||
} else {
|
||||
error = true;
|
||||
@@ -2939,8 +2935,8 @@ Bool_t PMsrHandler::HandleFitParameterEntry(PMsrLines &lines)
|
||||
if (PStringUtils::IsEqualNoCase(tokens[6], "none")) { // none
|
||||
param.fUpperBoundaryPresent = false;
|
||||
} else { // assuming a number
|
||||
if (PStringUtils::IsFloat(tokens[6])) {
|
||||
param.fUpperBoundary = PStringUtils::ToDouble(tokens[6]);
|
||||
param.fUpperBoundary = PStringUtils::ToDouble(tokens[6], &ok);
|
||||
if (ok) {
|
||||
param.fUpperBoundaryPresent = true;
|
||||
} else {
|
||||
error = true;
|
||||
@@ -3125,11 +3121,10 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines)
|
||||
if (tokens.size() < 3) {
|
||||
error = true;
|
||||
} else {
|
||||
if (PStringUtils::IsFloat(tokens[1])) {
|
||||
dval = PStringUtils::ToDouble(tokens[1]);
|
||||
if (dval <= 0.0)
|
||||
error = true;
|
||||
}
|
||||
bool ok = false;
|
||||
dval = PStringUtils::ToDouble(tokens[1], &ok);
|
||||
if (!ok || dval <= 0.0)
|
||||
error = true;
|
||||
if (!error) {
|
||||
global.SetRRFFreq(dval, tokens[2].c_str());
|
||||
if (global.GetRRFFreq(tokens[2].c_str()) == RRF_FREQ_UNDEF)
|
||||
@@ -3152,12 +3147,12 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines)
|
||||
if (tokens.size() < 2) {
|
||||
error = true;
|
||||
} else {
|
||||
if (PStringUtils::IsFloat(tokens[1])) {
|
||||
dval = PStringUtils::ToDouble(tokens[1]);
|
||||
bool ok = false;
|
||||
dval = PStringUtils::ToDouble(tokens[1], &ok);
|
||||
if (ok)
|
||||
global.SetRRFPhase(dval);
|
||||
} else {
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
} else if (iter->fLine.BeginsWith("data", TString::kIgnoreCase)) { // data
|
||||
if (tokens.size() < 3) {
|
||||
@@ -3178,15 +3173,12 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines)
|
||||
error = true;
|
||||
} else {
|
||||
for (UInt_t i=1; i<tokens.size(); i++) {
|
||||
if (PStringUtils::IsFloat(tokens[i])) {
|
||||
dval = PStringUtils::ToDouble(tokens[i]);
|
||||
if (dval >= 0.0)
|
||||
global.SetT0Bin(dval);
|
||||
else
|
||||
error = true;
|
||||
} else {
|
||||
bool ok = false;
|
||||
dval = PStringUtils::ToDouble(tokens[i], &ok);
|
||||
if (ok && dval >= 0.0)
|
||||
global.SetT0Bin(dval);
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (iter->fLine.BeginsWith("addt0", TString::kIgnoreCase)) { // addt0
|
||||
@@ -3194,15 +3186,12 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines)
|
||||
error = true;
|
||||
} else {
|
||||
for (UInt_t i=1; i<tokens.size(); i++) {
|
||||
if (PStringUtils::IsFloat(tokens[i])) {
|
||||
dval = PStringUtils::ToDouble(tokens[i]);
|
||||
if (dval >= 0.0)
|
||||
global.SetAddT0Bin(dval, addT0Counter, i-1);
|
||||
else
|
||||
error = true;
|
||||
} else {
|
||||
bool ok = false;
|
||||
dval = PStringUtils::ToDouble(tokens[i], &ok);
|
||||
if (ok && dval >= 0.0)
|
||||
global.SetAddT0Bin(dval, addT0Counter, i-1);
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
addT0Counter++;
|
||||
@@ -3241,8 +3230,10 @@ Bool_t PMsrHandler::HandleGlobalEntry(PMsrLines &lines)
|
||||
global.SetFitRangeInBins(true);
|
||||
} else { // fit given in time, i.e. fit <start> <end>, where <start>, <end> are given as doubles
|
||||
for (UInt_t i=1; i<3; i++) {
|
||||
if (PStringUtils::IsFloat(tokens[i]))
|
||||
global.SetFitRange(PStringUtils::ToDouble(tokens[i]), i-1);
|
||||
bool ok = false;
|
||||
const double range = PStringUtils::ToDouble(tokens[i], &ok);
|
||||
if (ok)
|
||||
global.SetFitRange(range, i-1);
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
@@ -3641,8 +3632,10 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines)
|
||||
error = true;
|
||||
} else {
|
||||
for (UInt_t i=1; i<tokens.size(); i++) {
|
||||
if (PStringUtils::IsFloat(tokens[i]))
|
||||
param.SetBkgFix(PStringUtils::ToDouble(tokens[i]), i-1);
|
||||
bool ok = false;
|
||||
const double bkgFix = PStringUtils::ToDouble(tokens[i], &ok);
|
||||
if (ok)
|
||||
param.SetBkgFix(bkgFix, i-1);
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
@@ -3696,15 +3689,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines)
|
||||
error = true;
|
||||
} else {
|
||||
for (UInt_t i=1; i<tokens.size(); i++) {
|
||||
if (PStringUtils::IsFloat(tokens[i])) {
|
||||
dval = PStringUtils::ToDouble(tokens[i]);
|
||||
if (dval >= 0.0)
|
||||
param.SetT0Bin(dval);
|
||||
else
|
||||
error = true;
|
||||
} else {
|
||||
bool ok = false;
|
||||
dval = PStringUtils::ToDouble(tokens[i], &ok);
|
||||
if (ok && dval >= 0.0)
|
||||
param.SetT0Bin(dval);
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3718,15 +3708,12 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines)
|
||||
error = true;
|
||||
} else {
|
||||
for (UInt_t i=1; i<tokens.size(); i++) {
|
||||
if (PStringUtils::IsFloat(tokens[i])) {
|
||||
dval = PStringUtils::ToDouble(tokens[i]);
|
||||
if (dval >= 0.0)
|
||||
param.SetAddT0Bin(dval, addT0Counter, i-1);
|
||||
else
|
||||
error = true;
|
||||
} else {
|
||||
bool ok = false;
|
||||
dval = PStringUtils::ToDouble(tokens[i], &ok);
|
||||
if (ok && dval >= 0.0)
|
||||
param.SetAddT0Bin(dval, addT0Counter, i-1);
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3773,8 +3760,10 @@ Bool_t PMsrHandler::HandleRunEntry(PMsrLines &lines)
|
||||
param.SetFitRangeInBins(true);
|
||||
} else { // fit given in time, i.e. fit <start> <end>, where <start>, <end> are given as doubles
|
||||
for (UInt_t i=1; i<3; i++) {
|
||||
if (PStringUtils::IsFloat(tokens[i]))
|
||||
param.SetFitRange(PStringUtils::ToDouble(tokens[i]), i-1);
|
||||
bool ok = false;
|
||||
const double range = PStringUtils::ToDouble(tokens[i], &ok);
|
||||
if (ok)
|
||||
param.SetFitRange(range, i-1);
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
@@ -4016,8 +4005,10 @@ Bool_t PMsrHandler::ParseFourierPhaseValueVector(PMsrFourierStructure &fourier,
|
||||
|
||||
// convert all acceptable tokens
|
||||
for (UInt_t i=1; i<tok.size(); i++) {
|
||||
if (PStringUtils::IsFloat(tok[i])) {
|
||||
fourier.fPhase.push_back(PStringUtils::ToDouble(tok[i]));
|
||||
bool ok = false;
|
||||
const double phase = PStringUtils::ToDouble(tok[i], &ok);
|
||||
if (ok) {
|
||||
fourier.fPhase.push_back(phase);
|
||||
} else {
|
||||
result = false;
|
||||
if (i>1) { // make sure that no 'phase val, parX' mixture is present
|
||||
@@ -4439,9 +4430,9 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines)
|
||||
continue;
|
||||
} else {
|
||||
for (UInt_t i=0; i<2; i++) {
|
||||
if (PStringUtils::IsFloat(tokens[i+1])) {
|
||||
fourier.fPlotRange[i] = PStringUtils::ToDouble(tokens[i+1]);
|
||||
} else {
|
||||
bool ok = false;
|
||||
fourier.fPlotRange[i] = PStringUtils::ToDouble(tokens[i+1], &ok);
|
||||
if (!ok) {
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
@@ -4472,11 +4463,10 @@ Bool_t PMsrHandler::HandleFourierEntry(PMsrLines &lines)
|
||||
break;
|
||||
case 3:
|
||||
for (UInt_t i=0; i<2; i++) {
|
||||
if (PStringUtils::IsFloat(tokens[i+1])) {
|
||||
fourier.fRangeForPhaseCorrection[i] = PStringUtils::ToDouble(tokens[i+1]);
|
||||
} else {
|
||||
bool ok = false;
|
||||
fourier.fRangeForPhaseCorrection[i] = PStringUtils::ToDouble(tokens[i+1], &ok);
|
||||
if (!ok)
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -4639,28 +4629,33 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines)
|
||||
} else {
|
||||
|
||||
// handle t_min
|
||||
if (PStringUtils::IsFloat(tokens[1]))
|
||||
param.fTmin.push_back(PStringUtils::ToDouble(tokens[1]));
|
||||
bool ok = false;
|
||||
const double tmin = PStringUtils::ToDouble(tokens[1], &ok);
|
||||
if (ok)
|
||||
param.fTmin.push_back(tmin);
|
||||
else
|
||||
error = true;
|
||||
|
||||
// handle t_max
|
||||
if (PStringUtils::IsFloat(tokens[2]))
|
||||
param.fTmax.push_back(PStringUtils::ToDouble(tokens[2]));
|
||||
const double tmax = PStringUtils::ToDouble(tokens[2], &ok);
|
||||
if (ok)
|
||||
param.fTmax.push_back(tmax);
|
||||
else
|
||||
error = true;
|
||||
|
||||
if (tokens.size() == 5) { // y-axis interval given as well
|
||||
|
||||
// handle y_min
|
||||
if (PStringUtils::IsFloat(tokens[3]))
|
||||
param.fYmin.push_back(PStringUtils::ToDouble(tokens[3]));
|
||||
const double ymin = PStringUtils::ToDouble(tokens[3], &ok);
|
||||
if (ok)
|
||||
param.fYmin.push_back(ymin);
|
||||
else
|
||||
error = true;
|
||||
|
||||
// handle y_max
|
||||
if (PStringUtils::IsFloat(tokens[4]))
|
||||
param.fYmax.push_back(PStringUtils::ToDouble(tokens[4]));
|
||||
const double ymax = PStringUtils::ToDouble(tokens[4], &ok);
|
||||
if (ok)
|
||||
param.fYmax.push_back(ymax);
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
@@ -4680,14 +4675,17 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines)
|
||||
for (UInt_t i=0; i<param.fRuns.size(); i++) {
|
||||
|
||||
// handle t_min
|
||||
if (PStringUtils::IsFloat(tokens[2*i+1]))
|
||||
param.fTmin.push_back(PStringUtils::ToDouble(tokens[2*i+1]));
|
||||
bool ok = false;
|
||||
const double tmin = PStringUtils::ToDouble(tokens[2*i+1], &ok);
|
||||
if (ok)
|
||||
param.fTmin.push_back(tmin);
|
||||
else
|
||||
error = true;
|
||||
|
||||
// handle t_max
|
||||
if (PStringUtils::IsFloat(tokens[2*i+2]))
|
||||
param.fTmax.push_back(PStringUtils::ToDouble(tokens[2*i+2]));
|
||||
const double tmax = PStringUtils::ToDouble(tokens[2*i+2], &ok);
|
||||
if (ok)
|
||||
param.fTmax.push_back(tmax);
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
@@ -4696,14 +4694,17 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines)
|
||||
if (tokens.size() == 2*param.fRuns.size() + 3) {
|
||||
|
||||
// handle y_min
|
||||
if (PStringUtils::IsFloat(tokens[2*param.fRuns.size()+1]))
|
||||
param.fYmin.push_back(PStringUtils::ToDouble(tokens[2*param.fRuns.size()+1]));
|
||||
bool ok = false;
|
||||
const double ymin = PStringUtils::ToDouble(tokens[2*param.fRuns.size()+1], &ok);
|
||||
if (ok)
|
||||
param.fYmin.push_back(ymin);
|
||||
else
|
||||
error = true;
|
||||
|
||||
// handle y_max
|
||||
if (PStringUtils::IsFloat(tokens[2*param.fRuns.size()+2]))
|
||||
param.fYmax.push_back(PStringUtils::ToDouble(tokens[2*param.fRuns.size()+2]));
|
||||
const double ymax = PStringUtils::ToDouble(tokens[2*param.fRuns.size()+2], &ok);
|
||||
if (ok)
|
||||
param.fYmax.push_back(ymax);
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
@@ -4716,14 +4717,17 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines)
|
||||
|
||||
if (tokens.size() == 3) { // i.e. use_fit_ranges ymin ymax
|
||||
// handle y_min
|
||||
if (PStringUtils::IsFloat(tokens[1]))
|
||||
param.fYmin.push_back(PStringUtils::ToDouble(tokens[1]));
|
||||
bool ok = false;
|
||||
const double ymin = PStringUtils::ToDouble(tokens[1], &ok);
|
||||
if (ok)
|
||||
param.fYmin.push_back(ymin);
|
||||
else
|
||||
error = true;
|
||||
|
||||
// handle y_max
|
||||
if (PStringUtils::IsFloat(tokens[2]))
|
||||
param.fYmax.push_back(PStringUtils::ToDouble(tokens[2]));
|
||||
const double ymax = PStringUtils::ToDouble(tokens[2], &ok);
|
||||
if (ok)
|
||||
param.fYmax.push_back(ymax);
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
@@ -4760,11 +4764,10 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines)
|
||||
error = true;
|
||||
} else {
|
||||
// get rrf frequency
|
||||
if (PStringUtils::IsFloat(tokens[1])) {
|
||||
param.fRRFFreq = PStringUtils::ToDouble(tokens[1]);
|
||||
} else {
|
||||
bool ok = false;
|
||||
param.fRRFFreq = PStringUtils::ToDouble(tokens[1], &ok);
|
||||
if (!ok)
|
||||
error = true;
|
||||
}
|
||||
// get unit
|
||||
if (PStringUtils::ContainsNoCase(tokens[2], "kHz"))
|
||||
param.fRRFUnit = RRF_UNIT_kHz;
|
||||
@@ -4787,25 +4790,25 @@ Bool_t PMsrHandler::HandlePlotEntry(PMsrLines &lines)
|
||||
error = true;
|
||||
} else {
|
||||
// get rrf phase
|
||||
if (PStringUtils::IsFloat(tokens[1])) {
|
||||
param.fRRFPhase = PStringUtils::ToDouble(tokens[1]);
|
||||
} else {
|
||||
if (PStringUtils::BeginsWithNoCase(tokens[1], "par")) { // parameter value
|
||||
Int_t no = 0;
|
||||
if (FilterNumber(tokens[1].c_str(), "par", 0, no)) {
|
||||
// check that the parameter is in range
|
||||
if (static_cast<Int_t>(fParam.size()) < no) {
|
||||
error = true;
|
||||
} else {
|
||||
// keep the parameter number in case parX was used
|
||||
param.fRRFPhaseParamNo = no;
|
||||
// get parameter value
|
||||
param.fRRFPhase = fParam[no-1].fValue;
|
||||
}
|
||||
bool ok = false;
|
||||
const double rrfPhase = PStringUtils::ToDouble(tokens[1], &ok);
|
||||
if (ok) {
|
||||
param.fRRFPhase = rrfPhase;
|
||||
} else if (PStringUtils::BeginsWithNoCase(tokens[1], "par")) { // parameter value
|
||||
Int_t no = 0;
|
||||
if (FilterNumber(tokens[1].c_str(), "par", 0, no)) {
|
||||
// check that the parameter is in range
|
||||
if (static_cast<Int_t>(fParam.size()) < no) {
|
||||
error = true;
|
||||
} else {
|
||||
// keep the parameter number in case parX was used
|
||||
param.fRRFPhaseParamNo = no;
|
||||
// get parameter value
|
||||
param.fRRFPhase = fParam[no-1].fValue;
|
||||
}
|
||||
} else {
|
||||
error = true;
|
||||
}
|
||||
} else {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
} else if (iter1->fLine.Contains("rrf_packing", TString::kIgnoreCase)) {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <charconv>
|
||||
#include <cstdlib>
|
||||
|
||||
@@ -161,12 +162,29 @@ int PStringUtils::ToInt(const std::string &str, bool *ok)
|
||||
* <p>Converts the leading part of the string to a double, mirroring
|
||||
* TString::Atof(). Returns 0.0 if no conversion is possible.
|
||||
*
|
||||
* <p>Like ToInt(), conversion errors can be reported through the optional
|
||||
* \a ok out-parameter: it is set to true when a value was parsed and to
|
||||
* false otherwise (no number present, or the value is out of range). This
|
||||
* lets callers distinguish a legitimate 0.0 from a failed conversion.
|
||||
* strtod() (rather than std::from_chars) is used so that the accepted input
|
||||
* set stays identical to TString::Atof() (leading whitespace is skipped, a
|
||||
* leading '+' is honoured, trailing characters are ignored). A null \a ok
|
||||
* preserves the historic fire-and-forget behaviour.
|
||||
*
|
||||
* \param str string to be converted
|
||||
* \return converted double value
|
||||
* \param ok optional out-parameter signalling conversion success
|
||||
* \return converted double value (0.0 on error)
|
||||
*/
|
||||
double PStringUtils::ToDouble(const std::string &str)
|
||||
double PStringUtils::ToDouble(const std::string &str, bool *ok)
|
||||
{
|
||||
return std::strtod(str.c_str(), nullptr);
|
||||
const char *begin = str.c_str();
|
||||
char *end = nullptr;
|
||||
errno = 0;
|
||||
const double value = std::strtod(begin, &end);
|
||||
if (ok != nullptr)
|
||||
*ok = (end != begin) && (errno != ERANGE);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -104,10 +104,15 @@ class PStringUtils
|
||||
* <p>Converts the leading part of the string to a double.
|
||||
* Mirrors TString::Atof(). Returns 0.0 if no conversion is possible.
|
||||
*
|
||||
* <p>If \a ok is non-null it is set to true when a value was parsed and
|
||||
* to false on error (no number, or value out of range), allowing callers
|
||||
* to distinguish a legitimate 0.0 from a failed conversion.
|
||||
*
|
||||
* @param str string to be converted
|
||||
* @return converted double value
|
||||
* @param ok optional out-parameter signalling conversion success
|
||||
* @return converted double value (0.0 on error)
|
||||
*/
|
||||
static double ToDouble(const std::string &str);
|
||||
static double ToDouble(const std::string &str, bool *ok = nullptr);
|
||||
|
||||
/**
|
||||
* <p>Case-insensitive full-string equality.
|
||||
|
||||
Reference in New Issue
Block a user