blob: 4f6fd2e3be03499524b97bab8419b9ae487a4262 [file] [log] [blame]
// Copyright (c) 2012, 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.
part of dart.core;
// TODO: Convert this abstract class into a concrete class double
// that uses the patch class functionality to account for the
// different platform implementations.
/**
* A double-precision floating point number.
*
* Representation of Dart doubles containing double specific constants
* and operations and specializations of operations inherited from
* [num]. Dart doubles are 64-bit floating-point numbers as specified in the
* IEEE 754 standard.
*
* The [double] type is contagious. Operations on [double]s return
* [double] results.
*
* It is a compile-time error for a class to attempt to extend or implement
* double.
*/
abstract class double extends num {
static const double NAN = 0.0 / 0.0;
static const double INFINITY = 1.0 / 0.0;
static const double NEGATIVE_INFINITY = -INFINITY;
static const double MIN_POSITIVE = 5e-324;
static const double MAX_FINITE = 1.7976931348623157e+308;
/**
* Truncating division operator.
*
* The result of the truncating division `a ~/ b` is equivalent to
* `(a / b).truncate()`.
*/
int operator ~/(double other);
/** Negate operator. */
double operator -();
/** Returns the absolute value of this [double]. */
double abs();
int compareTo(double other);
/**
* Returns the sign of the double's numerical value.
*
* Returns -1.0 if the value is less than zero,
* +1.0 if the value is greater than zero,
* and the value itself if it is -0.0, 0.0 or NaN.
*/
double get sign;
/**
* Returns the integer closest to `this`.
*
* Rounds away from zero when there is no closest integer:
* `(3.5).round() == 4` and `(-3.5).round() == -4`.
*
* If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError].
*/
int round();
/**
* Returns the greatest integer no greater than `this`.
*
* If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError].
*/
int floor();
/**
* Returns the least integer no smaller than `this`.
*
* If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError].
*/
int ceil();
/**
* Returns the integer obtained by discarding any fractional
* digits from `this`.
*
* If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError].
*/
int truncate();
/**
* Returns the integer double value closest to `this`.
*
* Rounds away from zero when there is no closest integer:
* `(3.5).roundToDouble() == 4` and `(-3.5).roundToDouble() == -4`.
*
* If this is already an integer valued double, including `-0.0`, or it is not
* a finite value, the value is returned unmodified.
*
* For the purpose of rounding, `-0.0` is considered to be below `0.0`,
* and `-0.0` is therefore considered closer to negative numbers than `0.0`.
* This means that for a value, `d` in the range `-0.5 < d < 0.0`,
* the result is `-0.0`.
*/
double roundToDouble();
/**
* Returns the greatest integer double value no greater than `this`.
*
* If this is already an integer valued double, including `-0.0`, or it is not
* a finite value, the value is returned unmodified.
*
* For the purpose of rounding, `-0.0` is considered to be below `0.0`.
* A number `d` in the range `0.0 < d < 1.0` will return `0.0`.
*/
double floorToDouble();
/**
* Returns the least integer double value no smaller than `this`.
*
* If this is already an integer valued double, including `-0.0`, or it is not
* a finite value, the value is returned unmodified.
*
* For the purpose of rounding, `-0.0` is considered to be below `0.0`.
* A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`.
*/
double ceilToDouble();
/**
* Returns the integer double value obtained by discarding any fractional
* digits from `this`.
*
* If this is already an integer valued double, including `-0.0`, or it is not
* a finite value, the value is returned unmodified.
*
* For the purpose of rounding, `-0.0` is considered to be below `0.0`.
* A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and
* in the range `0.0 < d < 1.0` it will return 0.0.
*/
double truncateToDouble();
/**
* Provide a representation of this [double] value.
*
* The representation is a number literal such that the closest double value
* to the representation's mathematical value is this [double].
*
* Returns "NaN" for the Not-a-Number value.
* Returns "Infinity" and "-Infinity" for positive and negative Infinity.
* Returns "-0.0" for negative zero.
*
* For all doubles, `d`, converting to a string and parsing the string back
* gives the same value again: `d == double.parse(d.toString())` (except when
* `d` is NaN).
*/
String toString();
/**
* Parse [source] as an double literal and return its value.
*
* Accepts an optional sign (`+` or `-`) followed by either the characters
* "Infinity", the characters "NaN" or a floating-point representation.
* A floating-point representation is composed of a mantissa and an optional
* exponent part. The mantissa is either a decimal point (`.`) followed by a
* sequence of (decimal) digits, or a sequence of digits
* optionally followed by a decimal point and optionally more digits. The
* (optional) exponent part consists of the character "e" or "E", an optional
* sign, and one or more digits.
*
* Leading and trailing whitespace is ignored.
*
* If the [source] is not a valid double literal, the [onError]
* is called with the [source] as argument, and its return value is
* used instead. If no `onError` is provided, a [FormatException]
* is thrown instead.
*
* The [onError] function is only invoked if [source] is a [String] with an
* invalid format. It is not invoked if the [source] is invalid for some
* other reason, for example by being `null`.
*
* Examples of accepted strings:
*
* "3.14"
* " 3.14 \xA0"
* "0."
* ".0"
* "-1.e3"
* "1234E+7"
* "+.12e-9"
* "-NaN"
*/
static double parse(String source, [double onError(String source)]) {
var result = _parse(source);
if (result == null) {
if (onError == null) throw new FormatException("Invalid double", source);
return onError(source);
}
return result;
}
static double _nativeParse(String str, int start, int end)
native "Double_parse";
static double _tryParseDouble(var str, var start, var end) {
assert(start < end);
const int _DOT = 0x2e; // '.'
const int _ZERO = 0x30; // '0'
const int _MINUS = 0x2d; // '-'
const int _N = 0x4e; // 'N'
const int _a = 0x61; // 'a'
const int _I = 0x49; // 'I'
const int _e = 0x65; // 'e'
int exponent = 0;
// Set to non-zero if a digit is seen. Avoids accepting ".".
bool digitsSeen = false;
// Added to exponent for each digit. Set to -1 when seeing '.'.
int exponentDelta = 0;
double doubleValue = 0.0;
double sign = 1.0;
int firstChar = str.codeUnitAt(start);
if (firstChar == _MINUS) {
sign = -1.0;
start++;
if (start == end) return null;
firstChar = str.codeUnitAt(start);
}
if (firstChar == _I) {
if (end == start + 8 && str.startsWith("nfinity", start + 1)) {
return sign * double.INFINITY;
}
return null;
}
if (firstChar == _N) {
if (end == start + 3 &&
str.codeUnitAt(start + 1) == _a &&
str.codeUnitAt(start + 2) == _N) {
return double.NAN;
}
return null;
}
int firstDigit = firstChar ^ _ZERO;
if (firstDigit <= 9) {
start++;
doubleValue = firstDigit.toDouble();
digitsSeen = true;
}
for (int i = start; i < end; i++) {
int c = str.codeUnitAt(i);
int digit = c ^ _ZERO; // '0'-'9' characters are now 0-9 integers.
if (digit <= 9) {
doubleValue = 10.0 * doubleValue + digit;
// Doubles at or above this value (2**53) might have lost precission.
const double MAX_EXACT_DOUBLE = 9007199254740992.0;
if (doubleValue >= MAX_EXACT_DOUBLE) return null;
exponent += exponentDelta;
digitsSeen = true;
} else if (c == _DOT && exponentDelta == 0) {
exponentDelta = -1;
} else if ((c | 0x20) == _e) {
i++;
if (i == end) return null;
// int._tryParseSmi treats its end argument as inclusive.
int expPart = int._tryParseSmi(str, i, end - 1);
if (expPart == null) return null;
exponent += expPart;
break;
} else {
return null;
}
}
if (!digitsSeen) return null; // No digits.
if (exponent == 0) return sign * doubleValue;
const P10 = POWERS_OF_TEN; // From shared library
if (exponent < 0) {
int negExponent = -exponent;
if (negExponent >= P10.length) return null;
return sign * (doubleValue / P10[negExponent]);
}
if (exponent >= P10.length) return null;
return sign * (doubleValue * P10[exponent]);
}
static double _parse(var str) {
int len = str.length;
int start = str._firstNonWhitespace();
if (start == len) return null; // All whitespace.
int end = str._lastNonWhitespace() + 1;
assert(start < end);
var result = _tryParseDouble(str, start, end);
if (result != null) return result;
return _nativeParse(str, start, end);
}
}