// 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 constexpr char kExponentChar = 'e'; static constexpr const char* kInfinitySymbol = "Infinity"; static constexpr const char* kNaNSymbol = "NaN"; void DoubleToCString(double d, char* buffer, int buffer_size) { const int kDecimalLow = -6; 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); 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, kInfinitySymbol, kNaNSymbol, kExponentChar, 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); } StringPtr DoubleToStringAsFixed(double d, int fraction_digits) { const int kMinFractionDigits = 0; const int kMaxFractionDigits = 20; const int kMaxDigitsBeforePoint = 20; // The boundaries are exclusive. const double kLowerBoundary = -1e21; const double kUpperBoundary = 1e21; // TODO(floitsch): remove the UNIQUE_ZERO flag when the test is updated. 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, kInfinitySymbol, kNaNSymbol, kExponentChar, 0, 0, 0, 0); // Last four values are ignored in fixed mode. char* buffer = Thread::Current()->zone()->Alloc(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()); } StringPtr DoubleToStringAsExponential(double d, int fraction_digits) { const int kMinFractionDigits = -1; // -1 represents shortest mode. const int kMaxFractionDigits = 20; 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, kInfinitySymbol, kNaNSymbol, kExponentChar, 0, 0, 0, 0); // Last four values are ignored in exponential mode. char* buffer = Thread::Current()->zone()->Alloc(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()); } StringPtr DoubleToStringAsPrecision(double d, int precision) { const int kMinPrecisionDigits = 1; const int kMaxPrecisionDigits = 21; const int kMaxLeadingPaddingZeroes = 6; const int kMaxTrailingPaddingZeroes = 0; 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, kInfinitySymbol, kNaNSymbol, kExponentChar, 0, 0, // Ignored in precision mode. kMaxLeadingPaddingZeroes, kMaxTrailingPaddingZeroes); char* buffer = Thread::Current()->zone()->Alloc(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, kInfinitySymbol, kNaNSymbol); int parsed_count = 0; *result = converter.StringToDouble(str, static_cast(length), &parsed_count); return (parsed_count == length); } IntegerPtr DoubleToInteger(Zone* zone, double val) { if (isinf(val) || isnan(val)) { const Array& args = Array::Handle(zone, Array::New(1)); args.SetAt(0, String::Handle(zone, String::New("Infinity or NaN toInt"))); Exceptions::ThrowByType(Exceptions::kUnsupported, args); } int64_t ival = 0; if (val <= static_cast(kMinInt64)) { ival = kMinInt64; } else if (val >= static_cast(kMaxInt64)) { ival = kMaxInt64; } else { // Representable in int64_t. ival = static_cast(val); } return Integer::New(ival); } } // namespace dart