223 lines
7.9 KiB
C++
223 lines
7.9 KiB
C++
/****************************************************************************
|
|
** Copyright (c) 2016, Fougue Ltd. <http://www.fougue.pro>
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
**
|
|
** 2. Redistributions in binary form must reproduce the above
|
|
** copyright notice, this list of conditions and the following
|
|
** disclaimer in the documentation and/or other materials provided
|
|
** with the distribution.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
****************************************************************************/
|
|
|
|
#include "google_doubleconversion.h"
|
|
|
|
#if GMIO_STR2FLOAT_LIB == GMIO_STR2FLOAT_LIB_DOUBLE_CONVERSION \
|
|
|| GMIO_FLOAT2STR_LIB == GMIO_FLOAT2STR_LIB_DOUBLE_CONVERSION
|
|
|
|
#include "c99_math_compat.h"
|
|
#include "string_ascii_utils.h"
|
|
#include "../../3rdparty/double-conversion/double-conversion.h"
|
|
|
|
#include <limits>
|
|
|
|
namespace {
|
|
|
|
template<typename T> T gmio_snan()
|
|
{ return std::numeric_limits<T>::signaling_NaN(); }
|
|
|
|
template<typename T> T gmio_inf()
|
|
{ return std::numeric_limits<T>::infinity(); }
|
|
|
|
template<typename T> struct FloatTraits {};
|
|
template<> struct FloatTraits<float> { static float zero() { return 0.f; } };
|
|
template<> struct FloatTraits<double> { static double zero() { return 0.; } };
|
|
|
|
template<typename T>
|
|
T gmio_generic_str2float_googledoubleconversion(const char* num, size_t numlen)
|
|
{
|
|
// Important note: implementation adapted from Qt5 source code
|
|
// qtbase/src/corelib/tools/qlocale_tools.cpp
|
|
// function asciiToDouble()
|
|
// License for gmio_str2float_googledoubleconversion():
|
|
/*
|
|
**
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
** Copyright (C) 2016 Intel Corporation.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the QtCore module of the Qt Toolkit.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 3 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 3 requirements
|
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
|
**
|
|
*/
|
|
|
|
bool ok = false;
|
|
int processed = 0;
|
|
T f = FloatTraits<T>::zero();
|
|
static const int conv_flags =
|
|
double_conversion::StringToDoubleConverter::NO_FLAGS;
|
|
|
|
if (*num == '\0') {
|
|
ok = false;
|
|
processed = 0;
|
|
return FloatTraits<T>::zero();
|
|
}
|
|
ok = true;
|
|
|
|
// We have to catch NaN before because we need NaN as marker for "garbage"
|
|
// in the libdouble-conversion case and, in contrast to libdouble-conversion
|
|
// or sscanf, we don't allow "-nan" or "+nan"
|
|
if (gmio_ascii_stricmp(num, "nan") == 0) {
|
|
processed = 3;
|
|
return gmio_snan<T>();
|
|
}
|
|
else if ((num[0] == '-' || num[0] == '+')
|
|
&& gmio_ascii_stricmp(num + 1, "nan") == 0)
|
|
{
|
|
processed = 0;
|
|
ok = false;
|
|
return FloatTraits<T>::zero();
|
|
}
|
|
|
|
// Infinity values are implementation defined in the sscanf case. In the
|
|
// libdouble-conversion case we need infinity as overflow marker
|
|
if (gmio_ascii_stricmp(num, "+inf") == 0) {
|
|
processed = 4;
|
|
return gmio_inf<T>();
|
|
} else if (gmio_ascii_stricmp(num, "inf") == 0) {
|
|
processed = 3;
|
|
return gmio_inf<T>();
|
|
} else if (gmio_ascii_stricmp(num, "-inf") == 0) {
|
|
processed = 4;
|
|
return -gmio_inf<T>();
|
|
}
|
|
|
|
static const double_conversion::StringToDoubleConverter conv(
|
|
conv_flags, 0., gmio_snan<T>(), NULL, NULL);
|
|
f = conv.StringToFloat(num, static_cast<int>(numlen), &processed);
|
|
if (!gmio_isfinite(f)) {
|
|
ok = false;
|
|
if (gmio_isnan(f)) {
|
|
// Garbage found. We don't accept it and return 0
|
|
processed = 0;
|
|
return FloatTraits<T>::zero();
|
|
} else {
|
|
// Overflow. That's not OK, but we still return infinity
|
|
return f;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// Check if underflow has occurred.
|
|
if (isZero(d)) {
|
|
for (int i = 0; i < processed; ++i) {
|
|
if (num[i] >= '1' && num[i] <= '9') {
|
|
// if a digit before any 'e' is not 0, then a non-zero number was intended.
|
|
ok = false;
|
|
return 0.0;
|
|
} else if (num[i] == 'e') {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return f;
|
|
}
|
|
|
|
template<typename T>
|
|
int gmio_generic_float2str_googledoubleconversion(
|
|
T value,
|
|
char *buff,
|
|
size_t bufflen,
|
|
gmio_float_text_format textformat,
|
|
uint8_t prec)
|
|
{
|
|
static const int flags =
|
|
double_conversion::DoubleToStringConverter::UNIQUE_ZERO
|
|
| double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN;
|
|
const bool lowercase_exp_char =
|
|
textformat == GMIO_FLOAT_TEXT_FORMAT_DECIMAL_LOWERCASE
|
|
|| textformat == GMIO_FLOAT_TEXT_FORMAT_SCIENTIFIC_LOWERCASE
|
|
|| textformat == GMIO_FLOAT_TEXT_FORMAT_SHORTEST_LOWERCASE;
|
|
const char exp_char = lowercase_exp_char ? 'e' : 'E';
|
|
const double_conversion::DoubleToStringConverter conv(
|
|
flags, "Infinity", "NaN", exp_char, -6, 21, 6, 0);
|
|
double_conversion::StringBuilder result_builder(
|
|
buff, static_cast<int>(bufflen));
|
|
switch (textformat) {
|
|
case GMIO_FLOAT_TEXT_FORMAT_DECIMAL_LOWERCASE:
|
|
case GMIO_FLOAT_TEXT_FORMAT_DECIMAL_UPPERCASE:
|
|
conv.ToFixed(value, prec, &result_builder);
|
|
break;
|
|
case GMIO_FLOAT_TEXT_FORMAT_SCIENTIFIC_LOWERCASE:
|
|
case GMIO_FLOAT_TEXT_FORMAT_SCIENTIFIC_UPPERCASE:
|
|
conv.ToExponential(value, prec, &result_builder);
|
|
break;
|
|
case GMIO_FLOAT_TEXT_FORMAT_SHORTEST_LOWERCASE:
|
|
case GMIO_FLOAT_TEXT_FORMAT_SHORTEST_UPPERCASE:
|
|
conv.ToPrecision(value, prec, &result_builder);
|
|
break;
|
|
}
|
|
return result_builder.position();
|
|
}
|
|
|
|
} // Anonymous namespace
|
|
|
|
float gmio_str2float_googledoubleconversion(const char* num, size_t numlen)
|
|
{
|
|
return ::gmio_generic_str2float_googledoubleconversion<float>(num, numlen);
|
|
}
|
|
|
|
double gmio_str2double_googledoubleconversion(const char* num, size_t numlen)
|
|
{
|
|
return ::gmio_generic_str2float_googledoubleconversion<double>(num, numlen);
|
|
}
|
|
|
|
int gmio_float2str_googledoubleconversion(
|
|
float value,
|
|
char *buff,
|
|
size_t bufflen,
|
|
gmio_float_text_format textformat,
|
|
uint8_t prec)
|
|
{
|
|
return gmio_generic_float2str_googledoubleconversion<float>(
|
|
value, buff, bufflen, textformat, prec);
|
|
}
|
|
|
|
int gmio_double2str_googledoubleconversion(
|
|
double value,
|
|
char* buff,
|
|
size_t bufflen,
|
|
enum gmio_float_text_format textformat,
|
|
uint8_t prec)
|
|
{
|
|
return gmio_generic_float2str_googledoubleconversion<double>(
|
|
value, buff, bufflen, textformat, prec);
|
|
}
|
|
|
|
#endif /* GMIO_STR2FLOAT_LIB == LIB_GOOGLE_DOUBLE_CONVERSION */
|