Added int-to-double.md
A rendered version of patch set 17 of this CL is available here: https://gist.github.com/eernstg/a70884d5249abbecb68d7771cb18ae37. Change-Id: If175f1787c11603bb4ed9bf89181cd8585afa54f Reviewed-on: https://dart-review.googlesource.com/68683 Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
## Feature: Evaluating integer literals as double values
|
||||
|
||||
**Author**: eernst@.
|
||||
|
||||
**Version**: 0.4 (2018-08-14).
|
||||
|
||||
**Status**: Background material, in language specification as of
|
||||
[d14b256](https://github.com/dart-lang/sdk/commit/d14b256e351464db352f361f1206e1415db65d9c).
|
||||
|
||||
**This document** is a feature specification of the support in Dart 2 for
|
||||
evaluating integer literals occurring in a context where the expected type
|
||||
is `double` to a value of type `double`.
|
||||
|
||||
|
||||
## Motivation
|
||||
|
||||
In a situation where a value of type `double` is required, e.g., as an
|
||||
actual argument to a constructor or function invocation, it may be
|
||||
convenient to write an integer literal because it is more concise, and
|
||||
the intention is clear. For instance:
|
||||
|
||||
```dart
|
||||
double one = 1; // OK, would have to be `1.0` without this feature.
|
||||
```
|
||||
|
||||
This mechanism only applies to integer literals, and only when the expected
|
||||
type is a type `T` such that `double` is assignable to `T` and `int` is not
|
||||
assignable to `T`; in particular, it applies when the expected type is
|
||||
`double`.
|
||||
|
||||
That is, the result of a computation (say, `a + b` or even a lone variable
|
||||
like `a` of type `int`) will never be converted to a double value
|
||||
implicitly, and it also doesn't happen when the expected type is a type
|
||||
variable declared in an enclosing scope, no matter whether that type
|
||||
variable in a given situation at run time has the value `double`. The one
|
||||
case where conversion does happen when the expected type is a type variable
|
||||
is when its upper bound is a subtype of `double` (including `double`
|
||||
itself). For example:
|
||||
|
||||
```dart
|
||||
class C<N extends num, NN extends double> {
|
||||
X foo<X>(X x) => x;
|
||||
double d1 = foo<double>(42); // OK.
|
||||
double d2 = foo(42); // OK, type argument inferred as `double`.
|
||||
num n1 = 42 as double; // OK, `42` evaluates to 42.0.
|
||||
N n2 = 42; // Error, neither `int` nor `double` assignable to `N`.
|
||||
NN n3 = 42; // OK, `int` not assignable, but `double` is.
|
||||
FutureOr<double> n4 = 42; // OK, same reason.
|
||||
N n5 = n1; // OK statically, dynamic error if `N` is `int`.
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Syntax
|
||||
|
||||
This feature has no effect on the grammar.
|
||||
|
||||
|
||||
## Static Analysis
|
||||
|
||||
Let _i_ be a lexical token which is syntactically an _integer literal_ (as
|
||||
defined in the language specification section 16.3 Numbers). If _i_ occurs
|
||||
as an expression in a context where the expected type `T` is such that
|
||||
`double` is assignable to `T` and `int` is not assignable to `T` then we
|
||||
will say that _i_ is a _double valued integer literal_.
|
||||
|
||||
The static type of a double valued integer literal is `double`.
|
||||
|
||||
The _unbounded integer value_ of an integer literal _i_ is the mathematical
|
||||
integer (that is, unlimited in size and precision) that corresponds to the
|
||||
numeral consisting of the digits of _i_, using radix 16 when _i_ is prefixed
|
||||
by `0x` or `0X`, and radix 10 otherwise.
|
||||
|
||||
It is a compile-time error if the unbounded integer value of a double
|
||||
valued integer literal is less than
|
||||
-(1−2<sup>−53</sup>) * 2<sup>1024</sup>
|
||||
and if it is greater
|
||||
than (1−2<sup>−53</sup>) * 2<sup>1024</sup>.
|
||||
|
||||
It is a compile-time error if the unbounded integer value of a double
|
||||
valued integer literal cannot be represented exactly as an IEEE 754
|
||||
double-precision value, assuming that the mantissa is extended with zeros
|
||||
until the precision is sufficiently high to unambiguously specify a single
|
||||
integer value.
|
||||
|
||||
*That is, we consider a IEEE 754 double-precision bit pattern to represent
|
||||
a specific number rather than an interval, namely the number which is
|
||||
obtained by extending the mantissa with zeros. In this case we are only
|
||||
interested in such bit patterns where the exponent part is large enough to
|
||||
make the represented number a whole number, which means that we will need
|
||||
to add a specific, finite number of zeros.*
|
||||
|
||||
*Consequently,
|
||||
`double d = 18446744073709551614;`
|
||||
has no error and it will initialize `d` to have the double value
|
||||
represented as 0x43F0000000000000. But
|
||||
`int i = 18446744073709551614;`
|
||||
is a compile-time error because 18446744073709551614 is too large to be
|
||||
represented as a 64 bit 2's complement number, and
|
||||
`double d = 18446744073709551615;`
|
||||
is a compile-time error because it cannot be represented exactly using the
|
||||
IEEE 754 double-precision format.*
|
||||
|
||||
|
||||
## Dynamic Semantics
|
||||
|
||||
At run time, evaluation of a double valued integer literal _i_ yields a
|
||||
value of type `double` which according to the IEEE 754 standard for
|
||||
double-precision numbers represents the unbounded integer value of _i_.
|
||||
|
||||
Signed zeros in IEEE 754 present an ambiguity. It is resolved by
|
||||
evaluating an expression which is a unary minus applied to a double valued
|
||||
integer literal whose unbounded integer value is zero to the IEEE 754
|
||||
representation of `-0.0`, and other occurrences of a double valued integer
|
||||
literal whose unbounded integer value is zero to the representation of
|
||||
`0.0`.
|
||||
|
||||
*We need not specify that the representation is extended with zeros as
|
||||
needed, because there is in any case only one IEEE 754 double-precision bit
|
||||
pattern that can be said to represent the given unbounded integer value: It
|
||||
would belong to the interval under any meaningful interpretation of such a
|
||||
bit pattern as an interval.*
|
||||
|
||||
|
||||
## Discussion
|
||||
|
||||
We have chosen to make it an error when a double valued integer literal
|
||||
cannot be represented exactly by an IEEE 754 double-precision encoding. We
|
||||
could have chosen to allow for a certain deviation from that, such that it
|
||||
would be allowed to have a double valued integer literal whose unbounded
|
||||
integer value would differ "somewhat" from the nearest representable value.
|
||||
|
||||
However, we felt that it would be misleading for developers to read a large
|
||||
double valued integer literal, probably assuming that every digit is
|
||||
contributing to the resulting double value, if in fact many of the digits
|
||||
are ignored, in the sense that they must be replaced by different digits in
|
||||
order to express the nearest representable IEEE double-precision value.
|
||||
|
||||
For instance,
|
||||
11692013098647223344361828061502034755750757138432 is represented as
|
||||
0x4A20000000000000, but so is
|
||||
11692013098647223344638182605120307455757075314823, which means that
|
||||
the 29 least significant digits are replaced by completely different
|
||||
digits. The former corresponds to an extension of the IEEE representation
|
||||
with a suitable number of zeros in the mantissa which will translate back
|
||||
to exactly that number, and the latter is just some other number which is
|
||||
close enough to have the same IEEE representation as the nearest
|
||||
representable value.
|
||||
|
||||
We expect such large double valued integer literals to occur very rarely,
|
||||
which means that such a situation will not arise very frequently. However,
|
||||
when it does arise it may be quite frustrating for developers to find a
|
||||
representable number, assuming that they start out with something like
|
||||
11692013098647223344638182605120307455757075314823. It is hence recommended
|
||||
that tools emit an error message where the nearest representable value is
|
||||
mentioned, such that a developer may copy it into the code in order to
|
||||
eliminate the error.
|
||||
|
||||
Another alternative would be to accept only those double valued integer
|
||||
literals whose unbounded integer value can be represented as a 2's
|
||||
complement bit pattern in 64 bits or in an unsigned 64 bit representation,
|
||||
that is, only those which are also accepted as integer literals of type
|
||||
`int`. This would ensure that developers avoid the situation where a large
|
||||
number of digits are incorrectly taken to imply a very high precision.
|
||||
This is especially relevant with decimal double valued integer literals,
|
||||
because they do not end in a large number of zeros, which make them "look"
|
||||
like a high-precision number where every digit means something. However, we
|
||||
felt that this reduction of the expressive power brings so few benefits
|
||||
that we preferred the approach where also "large numbers" could be
|
||||
expressed using a double valued integer literal.
|
||||
|
||||
|
||||
## Updates
|
||||
|
||||
* Version 0.4 (2018-08-14), adjusted rules to allow more expected types,
|
||||
such as `FutureOr<double>`, for double valued integer literals.
|
||||
|
||||
* Version 0.3 (2018-08-09), changed error rules such that it is now an
|
||||
error for a double valued integer literal to have an unbounded
|
||||
integer value which is not precisely representable.
|
||||
|
||||
* Version 0.2 (2018-08-08), added short discussion section.
|
||||
|
||||
* Version 0.1 (2018-08-07), initial version of this feature specification.
|
||||
Reference in New Issue
Block a user