blob: 7d136b3fd3f1b740a375372bcf65a44711cbf9fe [file] [log] [blame]
/****************************************************************
*
* The author of this software is David M. Gay.
*
* Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
* Copyright (C) 2002, 2005, 2006, 2007, 2008, 2010, 2012 Apple Inc.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*
***************************************************************/
/* Please send bug reports to David M. Gay (dmg at acm dot org,
* with " at " changed at "@" and " dot " changed to "."). */
/* On a machine with IEEE extended-precision registers, it is
* necessary to specify double-precision (53-bit) rounding precision
* before invoking strtod or dtoa. If the machine uses (the equivalent
* of) Intel 80x87 arithmetic, the call
* _control87(PC_53, MCW_PC);
* does this with many compilers. Whether this or another call is
* appropriate depends on the compiler; for this to work, it may be
* necessary to #include "float.h" or another system-dependent header
* file.
*/
#include "third_party/blink/renderer/platform/wtf/dtoa.h"
#include "base/containers/heap_array.h"
#include "base/numerics/safe_conversions.h"
#include "base/third_party/double_conversion/double-conversion/double-conversion.h"
#include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
namespace blink {
namespace {
double ParseDoubleFromLongString(base::span<const UChar> string,
size_t& parsed_length) {
size_t conversion_length = string.size();
auto conversion_buffer = base::HeapArray<LChar>::Uninit(conversion_length);
for (size_t i = 0; i < conversion_length; ++i) {
conversion_buffer[i] = IsASCII(string[i]) ? string[i] : 0;
}
return ParseDouble(base::span(conversion_buffer), parsed_length);
}
const double_conversion::StringToDoubleConverter& GetDoubleConverter() {
static double_conversion::StringToDoubleConverter converter(
double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES |
double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK,
0.0, 0, nullptr, nullptr);
return converter;
}
} // namespace
base::span<const LChar> DoubleToStringConverter::ToString(double d) {
double_conversion::StringBuilder builder(buffer_.data(), kBufferSize);
const double_conversion::DoubleToStringConverter& converter =
double_conversion::DoubleToStringConverter::EcmaScriptConverter();
converter.ToShortest(d, &builder);
base::span<char> result =
base::span(buffer_).first(static_cast<size_t>(builder.position()));
builder.Finalize();
return base::as_byte_span(result);
}
static inline base::span<const char>
FormatStringTruncatingTrailingZerosIfNeeded(
base::span<char, DoubleToStringConverter::kBufferSize> buffer,
double_conversion::StringBuilder& builder) {
size_t length = static_cast<size_t>(builder.position());
builder.Finalize();
base::span<char> result = buffer.first(length);
// If there is an exponent, stripping trailing zeros would be incorrect.
// FIXME: Zeros should be stripped before the 'e'.
if (std::ranges::find(result, 'e') != result.end()) {
return result;
}
size_t decimal_point_position = 0;
for (; decimal_point_position < length; ++decimal_point_position) {
if (buffer[decimal_point_position] == '.') {
break;
}
}
if (decimal_point_position == length) {
return result;
}
size_t truncated_length = length - 1;
for (; truncated_length > decimal_point_position; --truncated_length) {
if (result[truncated_length] != '0') {
break;
}
}
// No trailing zeros found to strip.
if (truncated_length == length - 1) {
return result;
}
// If we removed all trailing zeros, remove the decimal point as well.
if (truncated_length == decimal_point_position) {
DCHECK_GT(truncated_length, 0u);
--truncated_length;
}
// Truncate the buffer, and return the final result.
return result.first(truncated_length + 1);
}
base::span<const LChar> DoubleToStringConverter::ToStringWithFixedPrecision(
double d,
unsigned significant_figures) {
// Mimic String::format("%.[precision]g", ...), but use dtoas rounding
// facilities.
// "g": Signed value printed in f or e format, whichever is more compact for
// the given value and precision.
// The e format is used only when the exponent of the value is less than -4 or
// greater than or equal to the precision argument. Trailing zeros are
// truncated, and the decimal point appears only if one or more digits follow
// it.
// "precision": The precision specifies the maximum number of significant
// digits printed.
double_conversion::StringBuilder builder(buffer_.data(), kBufferSize);
const double_conversion::DoubleToStringConverter& converter =
double_conversion::DoubleToStringConverter::EcmaScriptConverter();
converter.ToPrecision(d, significant_figures, &builder);
// FIXME: Trailing zeros should never be added in the first place. The
// current implementation does not strip when there is an exponent, eg.
// 1.50000e+10.
return base::as_byte_span(
FormatStringTruncatingTrailingZerosIfNeeded(buffer_, builder));
}
base::span<const LChar> DoubleToStringConverter::ToStringWithFixedWidth(
double d,
unsigned decimal_places) {
// Mimic String::format("%.[precision]f", ...), but use dtoas rounding
// facilities.
// "f": Signed value having the form [ - ]dddd.dddd, where dddd is one or more
// decimal digits. The number of digits before the decimal point depends on
// the magnitude of the number, and the number of digits after the decimal
// point depends on the requested precision.
// "precision": The precision value specifies the number of digits after the
// decimal point. If a decimal point appears, at least one digit appears
// before it. The value is rounded to the appropriate number of digits.
double_conversion::StringBuilder builder(buffer_.data(), kBufferSize);
const double_conversion::DoubleToStringConverter& converter =
double_conversion::DoubleToStringConverter::EcmaScriptConverter();
converter.ToFixed(d, decimal_places, &builder);
base::span<char> result =
base::span(buffer_).first(static_cast<size_t>(builder.position()));
builder.Finalize();
return base::as_byte_span(result);
}
double ParseDouble(base::span<const LChar> string, size_t& parsed_length) {
int int_parsed_length = 0;
double d = GetDoubleConverter().StringToDouble(
reinterpret_cast<const char*>(string.data()),
base::saturated_cast<int>(string.size()), &int_parsed_length);
parsed_length = int_parsed_length;
return d;
}
double ParseDouble(base::span<const UChar> string, size_t& parsed_length) {
const size_t kConversionBufferSize = 64;
const size_t length = string.size();
if (length > kConversionBufferSize) {
return ParseDoubleFromLongString(string, parsed_length);
}
std::array<LChar, kConversionBufferSize> conversion_buffer;
for (size_t i = 0; i < length; ++i) {
conversion_buffer[i] =
IsASCII(string[i]) ? static_cast<LChar>(string[i]) : 0;
}
return ParseDouble(base::span(conversion_buffer).first(length),
parsed_length);
}
namespace internal {
void InitializeDoubleConverter() {
// Force initialization of static DoubleToStringConverter converter variable
// inside EcmaScriptConverter function while we are in single thread mode.
double_conversion::DoubleToStringConverter::EcmaScriptConverter();
GetDoubleConverter();
}
} // namespace internal
} // namespace blink