dart-sdk/runtime/vm/double_conversion.cc
vegorov@google.com a273179ac8 Use locale insensitive method to parse double literals.
Using strtod does not work because Dart grammar does not necessary match grammar used by strtod. Some locales (e.g. Russian and Danish) use comma instead of dot for radix point.

R=iposva@google.com
BUG=

Review URL: https://codereview.chromium.org//13529014

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@21074 260f80e4-7a28-3924-810f-c04153c831b5
2013-04-08 14:32:46 +00:00

191 lines
7.2 KiB
C++

// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/double_conversion.h"
#include "third_party/double-conversion/src/double-conversion.h"
#include "vm/exceptions.h"
#include "vm/globals.h"
#include "vm/object.h"
namespace dart {
static const char kDoubleToStringCommonExponentChar = 'e';
static const char* kDoubleToStringCommonInfinitySymbol = "Infinity";
static const char* kDoubleToStringCommonNaNSymbol = "NaN";
void DoubleToCString(double d, char* buffer, int buffer_size) {
static const int kDecimalLow = -6;
static const int kDecimalHigh = 21;
// The output contains the sign, at most kDecimalHigh - 1 digits,
// the decimal point followed by a 0 plus the \0.
ASSERT(buffer_size >= 1 + (kDecimalHigh - 1) + 1 + 1 + 1);
// Or it contains the sign, a 0, the decimal point, kDecimalLow '0's,
// 17 digits (the precision needed for doubles), plus the \0.
ASSERT(buffer_size >= 1 + 1 + 1 + kDecimalLow + 17 + 1);
// Alternatively it contains a sign, at most 17 digits (precision needed for
// any double), the decimal point, the exponent character, the exponent's
// sign, at most three exponent digits, plus the \0.
ASSERT(buffer_size >= 1 + 17 + 1 + 1 + 1 + 3 + 1);
static const int kConversionFlags =
double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN |
double_conversion::DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT |
double_conversion::DoubleToStringConverter::EMIT_TRAILING_ZERO_AFTER_POINT;
const double_conversion::DoubleToStringConverter converter(
kConversionFlags,
kDoubleToStringCommonInfinitySymbol,
kDoubleToStringCommonNaNSymbol,
kDoubleToStringCommonExponentChar,
kDecimalLow,
kDecimalHigh,
0, 0); // Last two values are ignored in shortest mode.
double_conversion::StringBuilder builder(buffer, buffer_size);
bool status = converter.ToShortest(d, &builder);
ASSERT(status);
char* result = builder.Finalize();
ASSERT(result == buffer);
}
RawString* DoubleToStringAsFixed(double d, int fraction_digits) {
static const int kMinFractionDigits = 0;
static const int kMaxFractionDigits = 20;
static const int kMaxDigitsBeforePoint = 20;
// The boundaries are exclusive.
static const double kLowerBoundary = -1e21;
static const double kUpperBoundary = 1e21;
// TODO(floitsch): remove the UNIQUE_ZERO flag when the test is updated.
static const int kConversionFlags =
double_conversion::DoubleToStringConverter::NO_FLAGS;
const int kBufferSize = 128;
USE(kMaxDigitsBeforePoint);
USE(kMaxFractionDigits);
USE(kLowerBoundary);
USE(kUpperBoundary);
USE(kMinFractionDigits);
USE(kMaxFractionDigits);
// The output contains the sign, at most kMaxDigitsBeforePoint digits,
// the decimal point followed by at most fraction_digits digits plus the \0.
ASSERT(kBufferSize >= 1 + kMaxDigitsBeforePoint + 1 + kMaxFractionDigits + 1);
ASSERT(kLowerBoundary < d && d < kUpperBoundary);
ASSERT(kMinFractionDigits <= fraction_digits &&
fraction_digits <= kMaxFractionDigits);
const double_conversion::DoubleToStringConverter converter(
kConversionFlags,
kDoubleToStringCommonInfinitySymbol,
kDoubleToStringCommonNaNSymbol,
kDoubleToStringCommonExponentChar,
0, 0, 0, 0); // Last four values are ignored in fixed mode.
char* buffer = Isolate::Current()->current_zone()->Alloc<char>(kBufferSize);
buffer[kBufferSize - 1] = '\0';
double_conversion::StringBuilder builder(buffer, kBufferSize);
bool status = converter.ToFixed(d, fraction_digits, &builder);
ASSERT(status);
return String::New(builder.Finalize());
}
RawString* DoubleToStringAsExponential(double d, int fraction_digits) {
static const int kMinFractionDigits = -1; // -1 represents shortest mode.
static const int kMaxFractionDigits = 20;
static const int kConversionFlags =
double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN;
const int kBufferSize = 128;
USE(kMinFractionDigits);
USE(kMaxFractionDigits);
// The output contains the sign, at most 1 digits, the decimal point followed
// by at most kMaxFractionDigits digits, the exponent-character, the
// exponent-sign and three exponent digits plus \0.
ASSERT(kBufferSize >= 1 + 1 + kMaxFractionDigits + 1 + 1 + 3 + 1);
ASSERT(kMinFractionDigits <= fraction_digits &&
fraction_digits <= kMaxFractionDigits);
const double_conversion::DoubleToStringConverter converter(
kConversionFlags,
kDoubleToStringCommonInfinitySymbol,
kDoubleToStringCommonNaNSymbol,
kDoubleToStringCommonExponentChar,
0, 0, 0, 0); // Last four values are ignored in exponential mode.
char* buffer = Isolate::Current()->current_zone()->Alloc<char>(kBufferSize);
buffer[kBufferSize - 1] = '\0';
double_conversion::StringBuilder builder(buffer, kBufferSize);
bool status = converter.ToExponential(d, fraction_digits, &builder);
ASSERT(status);
return String::New(builder.Finalize());
}
RawString* DoubleToStringAsPrecision(double d, int precision) {
static const int kMinPrecisionDigits = 1;
static const int kMaxPrecisionDigits = 21;
static const int kMaxLeadingPaddingZeroes = 6;
static const int kMaxTrailingPaddingZeroes = 0;
static const int kConversionFlags =
double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN;
const int kBufferSize = 128;
USE(kMinPrecisionDigits);
USE(kMaxPrecisionDigits);
// The output contains the sign, a potential leading 0, the decimal point,
// at most kMax{Leading|Trailing} padding zeroes, precision digits,
// the exponent-character, the exponent-sign, three exponent digits
// plus the \0.
// Note that padding and exponent are exclusive. We still add them up.
ASSERT(kBufferSize >= 1 + 1 + 1 + kMaxLeadingPaddingZeroes +
kMaxTrailingPaddingZeroes + kMaxPrecisionDigits + 1 + 1 + 3 + 1);
ASSERT(kMinPrecisionDigits <= precision && precision <= kMaxPrecisionDigits);
const double_conversion::DoubleToStringConverter converter(
kConversionFlags,
kDoubleToStringCommonInfinitySymbol,
kDoubleToStringCommonNaNSymbol,
kDoubleToStringCommonExponentChar,
0, 0, // Ignored in precision mode.
kMaxLeadingPaddingZeroes,
kMaxTrailingPaddingZeroes);
char* buffer = Isolate::Current()->current_zone()->Alloc<char>(kBufferSize);
buffer[kBufferSize - 1] = '\0';
double_conversion::StringBuilder builder(buffer, kBufferSize);
bool status = converter.ToPrecision(d, precision, &builder);
ASSERT(status);
return String::New(builder.Finalize());
}
bool CStringToDouble(const char* str, intptr_t length, double* result) {
if (length == 0) {
return false;
}
double_conversion::StringToDoubleConverter converter(
double_conversion::StringToDoubleConverter::NO_FLAGS,
0.0,
0.0,
kDoubleToStringCommonInfinitySymbol,
kDoubleToStringCommonNaNSymbol);
int parsed_count = 0;
*result = converter.StringToDouble(str,
static_cast<int>(length),
&parsed_count);
return (parsed_count == length);
}
} // namespace dart