-
Notifications
You must be signed in to change notification settings - Fork 205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow integer literals where a double value is expected. #4
Comments
LGTM! A couple of comments on the proposal:
Looks like a typo where we'd actually want In the 'Examples' section, the last example uses a very large integer literal to obtain the double value 'positive infinity'. We could insist that this must be expressed directly as a double (because it is actually not possible to represent the integer value of that large numeral exactly as a double value, and in particular it is not represented exactly by The example just before that uses a nearly-as-large integer literal to express the largest possible IEEE 754 double value that corresponds exactly to an integer. I would find it natural to make that an error as well, and only accept integer literals yielding a double value in cases where they would also be acceptable as integer literals (in all those other contexts where an implicit integer-to-double conversion does not apply). It's confusing, at least to me, that it's possible for a non-existing integer value to be converted.. |
Typo fixed. The very-large double value is intended to work. There is no conversion from integer to double here. None. The literal evaluates directly to a double value. |
OK (then I should have reloaded—the name was
Ah, that's a surprise to me, but I can see that it works: That literal never denotes an integer value, we just use it as input for the construction of a double value. OK! |
The Dart language specification has been updated to incorporate this design. |
Hopefully some pain here can be aleviated by this quickfix: https://dart-review.googlesource.com/c/sdk/+/75240. In the case where you enter |
I expect most double literals written as integers to be small integer values. The value needs to be above 2^53 for the precision to be a problem, and in that case, you probably want to be told that the number wouldn't have the value that you think. The work-around is trivial (add a |
Closing, this is tracked in implementation issue #20 |
Since [Dart 2.1](dart-lang/language#4), floating point values can be inferred from integer literals. Converting to the newer, more idiomatic Dart syntax since it's cleaner.
Since [Dart 2.1](dart-lang/language#4), floating point values can be inferred from integer literals. Converting to the newer, more idiomatic Dart syntax since it's cleaner.
Solution to #3.
Dart should allow an integer literal to denote a
double
value when it's used in a context which requires adouble
value.To do this, the meaning of the literal will have to depend on the expected type, aka. "the context type". The context type is already known to the compiler since Dart 2 uses that type for inference. The expected type may be empty (no requirement), but that still means that the expected type isn't exactly
double.
Proposal
Currently a valid integer literal, or an integer literal prefixed with a minus sign, always evaluates to instances of
int
. The (potentially signed) integer literal is invalid if its numerical value cannot be represented byint
(plus some edge cases for unsigned 64-bit integers).If the context type does not allow
int
to be assigned to it, the program fails to compile. That includes the case where the context type isdouble
, so programs likedouble x = 0;
are compile-time errors because of the type-invalid assignment.The current behavior is changed to:
and
This applies to both decimal and hexadecimal integer literals.
We recognize
-0
in a double context as evaluating to-0.0
.In all other contexts, the integer literal evaluates to an
int
instance like it currently does.Making the unrepresentable integer numeral an error allows the user to keep a simple mental model: Integer literals are always exact, floating point literals may not be. This matches the integer behavior, where an invalid
int
value is a compile-time error.This is a non-breaking change since the all programs that change behavior would have an integer literal in a double context, and therefore already be compile-time errors.
Examples
The following declarations are either valid (named "valid") or compile-time errors (named "bad").
Potential issues
This change is mostly simple and non-breaking, but there are potential issues.
Static typing
The change relies on static typing to decide the meaning of literals. We do that in a few other cases (instantiated tear-offs of generic methods), which can seem a bit magical, and means that the meaning of the expression depends on the type, and potentially on type inference.
Confusion because it doesn't work everywhere
This feature allows the use of integer literals as double values in some places. However, it is very restrictive in where that happens, and it's only applied to literals. That might be confusing to users who will expect integers to be usable as doubles in other situations, and it means that some refactorings are no longer valid. If they move a literal out of the double context, it might change meaning. Or, in other words, refactoring should type any variable that it introduces when extracting an expression, but that should generally be the case in Dart 2, to preserve the inference context.
Examples where context isn't expecting double:
The
doubleList
needs to be written as either:We can't easily change this, since the
num
-list is existing valid code and might be deliberate.This may be a usability pitfall to users, and we may be introducing new stumbling points to replace the ones we are fixing.
Exact integers are hard to write
We only allow integer literals that can be represented exactly by a double.
However, doubles are not always thought of as representing a single number. In some cases they are treated as if they represent a range, and any number in that range is represented by the same double value.
That affects
double.toString
, so there are double value where the non fractional digits of theirtoString
will not be a valid integer-literal-as-double. Since JavaScript does not add a final.0
when printing integer valued doubles, taking a double value in JavaScript and pasting its string representation into Dart may not be a valid double-typed integer literal.Example, in JavaScript console:
Now enter that into Dart:
This is a compile-time error because the value, as written, is not exactly representable as a double. The actual value of the JavaScript double is 974192668992941184.0, but the double-to-string conversion picks a representation with more trailing zeros, one that is still closer to the correct value than to any other valid double value.
This will likely be annoying.
We can, without issues, allow inexact literals, but it breaks the user expectation that an integer literal represents an exact number, and that any integer which is accepted on all platforms will meant the same thing everywhere. We already allow inexact values for double literals, but the trailing
.0
is a very clear hint to the reader that we are in double land.I recommend starting out with the strict rejection of any integer literal in a double context with a value that cannot be represented exactly by a double. We can then remove that restriction if experience tells us that it is too cumbersome to work with, but we cannot introduce a restriction after launching without it.
Implementation
Implementation is likely front-end only. Back-ends should just see a double literal.
The parser needs to allow more integer literals, so that finding the meaning of the integer literal can be delayed until the context type is known (past type inference).
Tools that process source code might need to be aware that this new combination is allowed.
JavaScript compilation
When compiling to JavaScript, integer literals will need to accept all valid non-fractional finite double values anyway, because that's what the
int
type can contain. They don't currently, we restrict to 64-bit values early, but we plan to change that. When that happens, the integer literals in a double context and the integer literals in a non-double context will behave exactly the same when compiled to JavaScript. That means that the code path must exist anyway, even without this feature, so adding this feature is unlikely to require large computations.Related work
Go lang constants.
The Go language numeric constants are "bignums" with at least 256 bits of precision, and all constant computations are performed at this large precision. They are only converted to the actual int and double types when the value is used in a dynamic computation. This differs from Dart where compile-time constant computations always use the same semantics as the run-time computations.
The text was updated successfully, but these errors were encountered: