mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 23:41:50 +00:00
fbbedf4f18
Add test. R=asiva@google.com, fschneider@google.com Review URL: https://codereview.chromium.org/2208473006 .
288 lines
9 KiB
Dart
288 lines
9 KiB
Dart
// 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.
|
|
|
|
class _Double implements double {
|
|
factory _Double.fromInteger(int value)
|
|
native "Double_doubleFromInteger";
|
|
|
|
Type get runtimeType => double;
|
|
|
|
// TODO: Make a stared static method for hashCode and _identityHashCode
|
|
// when semantics are corrected as described in:
|
|
// https://github.com/dart-lang/sdk/issues/2884
|
|
int get hashCode => (isNaN || isInfinite) ? 0 : toInt();
|
|
int get _identityHashCode => (isNaN || isInfinite) ? 0 : toInt();
|
|
|
|
double operator +(num other) {
|
|
return _add(other.toDouble());
|
|
}
|
|
double _add(double other) native "Double_add";
|
|
|
|
double operator -(num other) {
|
|
return _sub(other.toDouble());
|
|
}
|
|
double _sub(double other) native "Double_sub";
|
|
|
|
double operator *(num other) {
|
|
return _mul(other.toDouble());
|
|
}
|
|
double _mul(double other) native "Double_mul";
|
|
|
|
int operator ~/(num other) {
|
|
return _trunc_div(other.toDouble());
|
|
}
|
|
int _trunc_div(double other) native "Double_trunc_div";
|
|
|
|
double operator /(num other) {
|
|
return _div(other.toDouble());
|
|
}
|
|
double _div(double other) native "Double_div";
|
|
|
|
double operator %(num other) {
|
|
return _modulo(other.toDouble());
|
|
}
|
|
double _modulo(double other) native "Double_modulo";
|
|
|
|
double remainder(num other) {
|
|
return _remainder(other.toDouble());
|
|
}
|
|
double _remainder(double other) native "Double_remainder";
|
|
|
|
double operator -() native "Double_flipSignBit";
|
|
|
|
bool operator ==(other) {
|
|
if (!(other is num)) return false;
|
|
return _equal(other.toDouble());
|
|
}
|
|
bool _equal(double other) native "Double_equal";
|
|
bool _equalToInteger(int other) native "Double_equalToInteger";
|
|
bool operator <(num other) {
|
|
return other > this;
|
|
}
|
|
bool operator >(num other) {
|
|
return _greaterThan(other.toDouble());
|
|
}
|
|
bool _greaterThan(double other) native "Double_greaterThan";
|
|
bool operator >=(num other) {
|
|
return (this == other) || (this > other);
|
|
}
|
|
bool operator <=(num other) {
|
|
return (this == other) || (this < other);
|
|
}
|
|
double _addFromInteger(int other) {
|
|
return new _Double.fromInteger(other)._add(this);
|
|
}
|
|
double _subFromInteger(int other) {
|
|
return new _Double.fromInteger(other)._sub(this);
|
|
}
|
|
double _mulFromInteger(int other) {
|
|
return new _Double.fromInteger(other)._mul(this);
|
|
}
|
|
int _truncDivFromInteger(int other) {
|
|
return new _Double.fromInteger(other)._trunc_div(this);
|
|
}
|
|
double _moduloFromInteger(int other) {
|
|
return new _Double.fromInteger(other)._modulo(this);
|
|
}
|
|
double _remainderFromInteger(int other) {
|
|
return new _Double.fromInteger(other)._remainder(this);
|
|
}
|
|
bool _greaterThanFromInteger(int other)
|
|
native "Double_greaterThanFromInteger";
|
|
|
|
bool get isNegative native "Double_getIsNegative";
|
|
bool get isInfinite native "Double_getIsInfinite";
|
|
bool get isNaN native "Double_getIsNaN";
|
|
bool get isFinite => !isInfinite && !isNaN; // Can be optimized.
|
|
|
|
double abs() {
|
|
// Handle negative 0.0.
|
|
if (this == 0.0) return 0.0;
|
|
return this < 0.0 ? -this : this;
|
|
}
|
|
|
|
double get sign {
|
|
if (this > 0.0) return 1.0;
|
|
if (this < 0.0) return -1.0;
|
|
return this; // +/-0.0 or NaN.
|
|
}
|
|
|
|
int round() => roundToDouble().toInt();
|
|
int floor() => floorToDouble().toInt();
|
|
int ceil () => ceilToDouble().toInt();
|
|
int truncate() => truncateToDouble().toInt();
|
|
|
|
double roundToDouble() native "Double_round";
|
|
double floorToDouble() native "Double_floor";
|
|
double ceilToDouble() native "Double_ceil";
|
|
double truncateToDouble() native "Double_truncate";
|
|
|
|
num clamp(num lowerLimit, num upperLimit) {
|
|
if (lowerLimit is! num) {
|
|
throw new ArgumentError.value(lowerLimit, "lowerLimit", "not a number");
|
|
}
|
|
if (upperLimit is! num) {
|
|
throw new ArgumentError.value(upperLimit, "upperLimit", "not a number");
|
|
}
|
|
|
|
if (lowerLimit.compareTo(upperLimit) > 0) {
|
|
throw new ArgumentError(lowerLimit);
|
|
}
|
|
if (lowerLimit.isNaN) return lowerLimit;
|
|
if (this.compareTo(lowerLimit) < 0) return lowerLimit;
|
|
if (this.compareTo(upperLimit) > 0) return upperLimit;
|
|
return this;
|
|
}
|
|
|
|
int toInt() native "Double_toInt";
|
|
num _toBigintOrDouble() { return this; }
|
|
double toDouble() { return this; }
|
|
|
|
static const int CACHE_SIZE_LOG2 = 3;
|
|
static const int CACHE_LENGTH = 1 << (CACHE_SIZE_LOG2 + 1);
|
|
static const int CACHE_MASK = CACHE_LENGTH - 1;
|
|
// Each key (double) followed by its toString result.
|
|
static final List _cache = new List(CACHE_LENGTH);
|
|
static int _cacheEvictIndex = 0;
|
|
|
|
String _toString() native "Double_toString";
|
|
|
|
String toString() {
|
|
// TODO(koda): Consider starting at most recently inserted.
|
|
for (int i = 0; i < CACHE_LENGTH; i += 2) {
|
|
// Need 'identical' to handle negative zero, etc.
|
|
if (identical(_cache[i], this)) {
|
|
return _cache[i + 1];
|
|
}
|
|
}
|
|
// TODO(koda): Consider optimizing all small integral values.
|
|
if (identical(0.0, this)) {
|
|
return "0.0";
|
|
}
|
|
String result = _toString();
|
|
// Replace the least recently inserted entry.
|
|
_cache[_cacheEvictIndex] = this;
|
|
_cache[_cacheEvictIndex + 1] = result;
|
|
_cacheEvictIndex = (_cacheEvictIndex + 2) & CACHE_MASK;
|
|
return result;
|
|
}
|
|
|
|
String toStringAsFixed(int fractionDigits) {
|
|
// See ECMAScript-262, 15.7.4.5 for details.
|
|
|
|
if (fractionDigits is! int) {
|
|
throw new ArgumentError.value(
|
|
fractionDigits, "fractionDigits", "not an integer");
|
|
}
|
|
// Step 2.
|
|
if (fractionDigits < 0 || fractionDigits > 20) {
|
|
throw new RangeError.range(fractionDigits, 0, 20, "fractionDigits");
|
|
}
|
|
|
|
// Step 3.
|
|
double x = this;
|
|
|
|
// Step 4.
|
|
if (isNaN) return "NaN";
|
|
|
|
// Step 5 and 6 skipped. Will be dealt with by native function.
|
|
|
|
// Step 7.
|
|
if (x >= 1e21 || x <= -1e21) {
|
|
return x.toString();
|
|
}
|
|
|
|
return _toStringAsFixed(fractionDigits);
|
|
}
|
|
String _toStringAsFixed(int fractionDigits) native "Double_toStringAsFixed";
|
|
|
|
String toStringAsExponential([int fractionDigits]) {
|
|
// See ECMAScript-262, 15.7.4.6 for details.
|
|
|
|
// The EcmaScript specification checks for NaN and Infinity before looking
|
|
// at the fractionDigits. In Dart we are consistent with toStringAsFixed and
|
|
// look at the fractionDigits first.
|
|
|
|
// Step 7.
|
|
if (fractionDigits != null) {
|
|
if (fractionDigits is! int) {
|
|
throw new ArgumentError.value(
|
|
fractionDigits, "fractionDigits", "not an integer");
|
|
}
|
|
if (fractionDigits < 0 || fractionDigits > 20) {
|
|
throw new RangeError.range(fractionDigits, 0, 20, "fractionDigits");
|
|
}
|
|
}
|
|
|
|
if (isNaN) return "NaN";
|
|
if (this == double.INFINITY) return "Infinity";
|
|
if (this == -double.INFINITY) return "-Infinity";
|
|
|
|
// The dart function prints the shortest representation when fractionDigits
|
|
// equals null. The native function wants -1 instead.
|
|
fractionDigits = (fractionDigits == null) ? -1 : fractionDigits;
|
|
|
|
return _toStringAsExponential(fractionDigits);
|
|
}
|
|
String _toStringAsExponential(int fractionDigits)
|
|
native "Double_toStringAsExponential";
|
|
|
|
String toStringAsPrecision(int precision) {
|
|
// See ECMAScript-262, 15.7.4.7 for details.
|
|
|
|
// The EcmaScript specification checks for NaN and Infinity before looking
|
|
// at the fractionDigits. In Dart we are consistent with toStringAsFixed and
|
|
// look at the fractionDigits first.
|
|
|
|
if (precision is! int) {
|
|
throw new ArgumentError.value(precision, "precision", "not an integer");
|
|
}
|
|
// Step 8.
|
|
if (precision < 1 || precision > 21) {
|
|
throw new RangeError.range(precision, 1, 21, "precision");
|
|
}
|
|
|
|
if (isNaN) return "NaN";
|
|
if (this == double.INFINITY) return "Infinity";
|
|
if (this == -double.INFINITY) return "-Infinity";
|
|
|
|
return _toStringAsPrecision(precision);
|
|
}
|
|
String _toStringAsPrecision(int fractionDigits)
|
|
native "Double_toStringAsPrecision";
|
|
|
|
// Order is: NaN > Infinity > ... > 0.0 > -0.0 > ... > -Infinity.
|
|
int compareTo(num other) {
|
|
const int EQUAL = 0, LESS = -1, GREATER = 1;
|
|
if (this < other) {
|
|
return LESS;
|
|
} else if (this > other) {
|
|
return GREATER;
|
|
} else if (this == other) {
|
|
if (this == 0.0) {
|
|
bool thisIsNegative = isNegative;
|
|
bool otherIsNegative = other.isNegative;
|
|
if (thisIsNegative == otherIsNegative) {
|
|
return EQUAL;
|
|
}
|
|
return thisIsNegative ? LESS : GREATER;
|
|
} else {
|
|
return EQUAL;
|
|
}
|
|
} else if (isNaN) {
|
|
return other.isNaN ? EQUAL : GREATER;
|
|
} else {
|
|
// Other is NaN.
|
|
return LESS;
|
|
}
|
|
}
|
|
|
|
static const int _FRACTIONAL_BITS = // Bits to keep after the decimal point.
|
|
const int.fromEnvironment("doubleFractionalBits", defaultValue: 20);
|
|
static const double _BIAS = 1.5 * (1 << (52 - _FRACTIONAL_BITS));
|
|
|
|
// Returns this with only _FRACTIONAL_BITS bits after the decimal point.
|
|
double get p => this + _BIAS - _BIAS;
|
|
}
|