<p>In some languages, a number literal's type can be inferred from nearby expressions. For example, <code>1u64 + 2</code> might give a <code>u64</code> value of <code>3</code>, since the <code>2</code>'s type is inferred to be <code>u64</code>. This can similarly be done with floats/doubles. One language I've used that works like this is Rust; a number literal without a decimal point is an int, and a number literal with a decimal point is a float or double.</p>
<p>However, this can make working with floats a little inconvenient. E.g., in a weakly typed language, I could write an expression like <code>(some_float + 1) / 2</code>; however, since <code>1</code> and <code>2</code> are int literals in Rust, this gives an error:</p>
<pre><code>let out0 = (some_float + 1) / 2;
                       ^ no implementation for `f64 + {integer}`
</code></pre>
<p>This makes it necessary to write something like <code>(some_float + 1.) / 2.</code>. So, my idea was to make number literals with a decimal point always act as floats/doubles, but allow decimal-point-less literals to be treated either as ints or floats.</p>
<p>I'm not sure if there any languages which take this approach. Are there any potential downsides or ambiguities that could arise from this?</p>

<syntax><type-inference><floating-point><literals>
What are the consequences of allowing decimal-point-less number literals to be interpreted as floats?
CC BY-SA 4.0
2023-12-08T16:02:35.153Z
Haskell has overloaded number literals (which are a bit more general than your idea), you might want to look into that
user
CC BY-SA 4.0
2023-12-08T16:42:44.890Z
Another option is to have different operators for integer and floating-point operations: this would inform the type checker about the expected types of the arguments. But syntactically this is probably more cumbersome than giving number literals unambiguous types. OCaml uses `+` and `+.` for example and it is not popular.
Christian Lindig
CC BY-SA 4.0
2023-12-08T17:32:54.257Z
Perhaps, if your language allows, you could overload the + operator for both floats and integers to take an operand of a different type.
FireTheLost
CC BY-SA 4.0
2023-12-08T19:03:25.947Z
The meaning of the first paragraph is unclear to me. You suggest that `2`'s type is inferred from the nearby `u64` qualifier, then you say that Rust works that way, then you say (and show by example) that Rust doesn't work that way.
benrg
CC BY-SA 4.0
2023-12-08T19:12:50.360Z
@benrg It does work that way for ints; `2` could be an `i32` or a `u8` or a `u64` and so on. But unless there's a `.` it won't interpret it as a float.
rydwolf
CC BY-SA 4.0
2023-12-08T20:02:15.857Z
@benrg I am in fact talking about the Haskell way of doing it
rydwolf
CC BY-SA 4.0
2023-12-08T20:04:24.660Z
@RydwolfPrograms Okay, I see what you mean.
benrg
CC BY-SA 4.0
2023-12-10T12:48:24.903Z
XPath (version 2.0+) takes exactly this approach. 42 is an integer, 42.1 is a decimal, 42e0 is a double. Yes, there are plenty of downsides, but probably no more than any other approach. Note that this type inference is done only for literals within the XPath expression itself, not for data read from a source (XML) document, so it's entirely under the control of the XPath author. Note that XPath allows arithmetic and comparison operators on mixed numeric types; that's convenient but carries its own set of problems, such as transitivity.
Michael Kay
CC BY-SA 4.0
2024-04-03T16:55:36.760Z
I believe Go does something similar to this
CoffeeTableEspresso
<h3>Inexact representation</h3>
<p><code>1</code> is <code>1</code> in <code>u64</code>, <code>u32</code>, <code>u16</code> and even <code>u1</code>. That is, they compare equals in all these types.</p>
<p>But floats don't have this property. <code>0.1f16</code> is represented very differently in memory than <code>0.1f32</code> or <code>0.1f64</code>, and almost certainly does <em>not</em> compare equals in any of these cases. This is the principal reason that a lot of languages don't treat them as the same as in the integral types.</p>
<h3>Language ergonomics</h3>
<p>But your example of <code>(some_float + 1) / 2</code>, 1 and 2 almost certainly <em>have exact representations</em> in <code>some_float</code>'s format, and if the language detects that, it can overcome the very reason the code is rejected in the first place.</p>
<p>Detecting if a particular <code>int</code> has exact representation in some float format normally goes over what is expected of the parser/compiler of the language, but this question is about alternatives.</p>
<h3>Accept exact representations</h3>
<p>Instead of rejecting <code>(some_float some_operation some_int)</code> in all cases, at parser level, run an extra step of detecting if the <code>some_int</code> have exact representation on <code>some_float</code> format, and accept that.</p>
<p>Or, in other cases, reject with a much more explanatory error diagnostic. Something like <code>some_int has no exact representation in f64</code>.</p>
<hr />
<p><sup>Note that placing a <code>.</code> on integer literals or operators to &quot;resolve&quot; this compilation error is a bit misleading, as there is no guarantee that any float literal will have an exact representation, in any floating point format. It only directs the compiler to call some float parsing code. Having the compiler reject this construct in <em>some</em> cases, with a very specific error message, could pose as an ideal starting point in educating programmers about the inexact nature of <em>most</em> float literals.</sup></p>

CC BY-SA 4.0
2023-12-08T19:49:10.577Z
2023-12-08T22:14:30.900Z
2023-12-08T22:14:30.900Z
André L F S Bacci
CC BY-SA 4.0
2023-12-09T20:56:19.170Z
I don't buy the argument about inexact representation. Sure, `0.1` represents different exact values depending on the level of precision, but the question is asking about "*decimal-point-less number literals*". Surely `1f16`, `1f32`, `1f64`, `1u1`, `1u32`, `1i64` etc all represent the exact same value?
Bergi
CC BY-SA 4.0
2023-12-10T14:36:34.773Z
@Bergi For small integral values yes, they all represent the same ‘exact’ numeric value (although the floats do so slightly differently from each other). But small integral values are never the issue with floats, large integral values are, and the definition of ‘large’ depends on your float size. For example `4000f16` is likely not the exact same number as `4000u16`, because most 16-bit float representations don’t have enough significand bits to represent the integer 4000 exactly.
Austin Hemmelgarn
CC BY-SA 4.0
2023-12-10T14:39:53.907Z
@AustinHemmelgarn Same issue with `4000u8` though
Bergi
CC BY-SA 4.0
2023-12-11T16:01:07.297Z
@Bergi No. `4000u8` is an error, 4000 is not representable as an `u8`, but `4000f16` is (so far) not an error, because 4000 is (imprecisely, but validly) representable as an `f16`.
Pablo H
CC BY-SA 4.0
2023-12-15T02:48:37.623Z
@Bergi There’s a difference between being able to provide an exact representation, being able to provide an approximate representation, and not being able to provide any representation. The integer 4000 cannot be represented in a standard `u8` type (it will overflow or be truncated), but it can be approximated with most typical `f16` types even if they cannot provide an _exact_ representation.
Austin Hemmelgarn
<p>It seems to me that several ambiguities arise.</p>
<p>If your language has function overloading, then given <code>fn foo(u64);</code> and <code>fn foo(f64);</code> with different behaviors, which one is called by <code>foo(3)</code>?</p>
<p>If you have type inference, then given <code>let x = 3;</code>, what is the type of <code>x</code>?</p>
<p>Given <code>f64 y = (9223372036854775808 + 9223372036854775808) as f64;</code>, does <code>y</code> get assigned the value <code>18446744073709551616.0</code>, or <code>0.0</code>, or does it cause an integer overflow trap?</p>

CC BY-SA 4.0
2023-12-09T05:50:40.060Z
2023-12-09T05:50:40.060Z
Nate Eldredge
CC BY-SA 4.0
2023-12-10T00:59:01.760Z
"If you have type inference, then given `let x = 3;`, what is the type of `x`?" This is actually only an issue in languages with half-baked inference. In Haskell, a local `let x = 3` causes the type of `x` to be inferred from how `x` is used, i.e. the expression `let x = 3 in x + (5 :: Int)` has `x :: Int` whereas `let x = 3 in x + (pi :: Double)` has `x :: Double`.
leftaroundabout
<p>Haskell does this. In Haskell, the type of the literal 2 is <code>Num a =&gt; a</code> which can be instantiated as Int, or Float, or some matrix type, or whatever else satisfies Num</p>

CC BY-SA 4.0
2023-12-18T19:06:41.853Z
2023-12-18T19:06:41.853Z
Александр Залялов
CC BY-SA 4.0
2023-12-19T11:09:04.757Z
As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer).
Community Compiler
<p>There is a syntactic drawback and I created a small Tyr example explaining it <a href="https://github.com/feldentm/TyrExamples/blob/master/floatLiterals/mar.tyr" rel="nofollow noreferrer">here</a>.
The thing is that, if you use <code>.</code> for member access, you need to choose if <code>0.f</code> is <code>0</code> <code>.</code> <code>f</code> or a single float literal with value <code>0</code>. My personal preference is to use member access with <code>.</code> and allow member access on integers. If you agree on that, you will see why <code>0.</code> should be interpreted as <code>0</code> <code>.</code> wrt. to tokenization. If you now think about something like &quot;let's fix it later in the parser&quot; you will run into all sorts of ambiguities in the <code>0.f</code> case.</p>
<p>But the question itself is valid. Hence, Tyr interprets <code>0f</code> as float32 0.
Wrt. to the question, I simply exchanged the <code>.</code> with an <code>f</code> to fix the ambiguity with member access. Actually, these ambiguities are far worse than all the implicit conversions between signed, unsigned and float.</p>
<p>One could also add <code>0d</code> for double, but I doubt that it is really needed because most users will be used to writing <code>0.0</code>.</p>

CC BY-SA 4.0
2023-12-09T09:39:49.617Z
2023-12-09T09:39:49.617Z
feldentm
CC BY-SA 4.0
2023-12-09T12:31:18.603Z
Javascript also uses `.` for member access and allows member accesses on numbers, but syntactically the `.` is part of the number token (based on the longest-match rule). So I don't buy your argument that *"If you agree on that, you will see why `0.` should be interpreted as `0` `.`"* ─ Javascript's approach doesn't seem to cause any problems. In practice I think it's quite rare that you need to access a member of a literal, because the result will be some constant value which you could have written instead, but if you do ever need this then having to write `(0).foo` is not a dealbreaker.
kaya3
CC BY-SA 4.0
2023-12-09T13:24:09.027Z
Or you can write `0..foo`, which is a little more confusing at first but technically shorter.
Bbrk24
CC BY-SA 4.0
2023-12-09T13:43:16.373Z
I would need to have a look at how JavaScript does it and what the limitations really are. My experience with moving token distinction to the parser is that it always creates odd corner cases like no longer being able to split lines after a `.` without changing semantics. For me `(0).f` not being equal to `0.f` would be a deal breaker. The `0..f` comment is not valid as it would have the semantics of `0f.f` in contrast to the intended `(0).f`. Should I extend my answer by these two boundary conditions?
feldentm
CC BY-SA 4.0
2023-12-09T22:57:33.287Z
@feldentm it seems a bit dramatic for something that minor to be a "deal-breaker". As Bbrk said, it's likely you'll almost never need to access a member of a constant, and when you do, wrapping it in parentheses is perfectly intuitive and works fine. Also, I'm not sure I understand what you mean about moving logic to the parser; "a `.` after a number is part of the same token" is a simple rule that works just fine right?
rydwolf
CC BY-SA 4.0
2023-12-10T06:55:43.680Z
@RydwolfPrograms your position on `.` makes sense if you consider `0..f` and `(0).f` good solutions for something like `(0).show()`. If you want it to be `0.show()` you need to do some parser and magic words logic. But, the more we discuss it, the more my position against having it is that `0.show()` yields a parse error and  `0..show()` yields `"0.0f"`.
feldentm
CC BY-SA 4.0
2023-12-10T18:07:35.023Z
@feldentm In my opinion `0..f` should be a syntax error; I'd lex that as a float with an extraneous dot in it, personally, in the same way as `3.141.59`
rydwolf
CC BY-SA 4.0
2023-12-10T19:35:07.997Z
ok, now I would be really interested to see how your lexer work and what token stream it'd produce
feldentm
CC BY-SA 4.0
2023-12-14T14:21:44.153Z
@feldentm It would just parse number literals as `\d+(\.\d*)?|\.\d+` (possibly with a `\w*` or something at the end for the type indicator thing), I've done this in all my languages. I'm not even sure what the alternative would be, tbh.
rydwolf
<p>In Swift, “3” on its own without the quotes is not a number, it is an IntegerConvertibleLiteral (or something like that). Type interference is complicated. In</p>
<p>let x: Int8 = 3</p>
<p>it becomes an Int8. In</p>
<p>let d: Double = 3 + 5</p>
<p>3 + 5 must be Double, therefore 3 and 5 must be Double, so 3 becomes a Double. 3 prefers to be an Int during type inference and 3.0 prefers to be a Double, so</p>
<pre><code>let x = 3
</code></pre>
<p>turns 3 and x into Int since there is no stronger inference rule.</p>

CC BY-SA 4.0
2023-12-13T20:35:02.487Z
2023-12-13T20:35:02.487Z
gnasher729
<h2>Negative zero</h2>
<p>This <em>barely</em> feels like an answer, but it's the biggest &quot;wut&quot; I've noticed with it so far in Swift.</p>
<p>In Swift, if there is no separation between a unary minus and the number, and no method calls on the literal, the minus sign is interpreted as part of the literal. So, <code>let x: Double = -0</code> is interpreted as converting the integer -0 (i.e. 0) to a Double, giving 0.0, while <code>let y: Double = -(0)</code> is interpreted as converting the integer 0 to a Double and then negating it, giving -0.0. Writing <code>-0.0</code> avoids this by always being negative zero.</p>

CC BY-SA 4.0
2023-12-09T03:49:35.293Z
2023-12-09T03:49:35.293Z
Bbrk24
CC BY-SA 4.0
2023-12-10T06:08:01.833Z
Haskell allows decimal-point-less number literals to be interpreted as floats, but it doesn't have this problem because it doesn't first treat the literal as an integer to later convert. Rather, it understands the type as Double from the start by using the surrounding context: `let { x :: Double; x = -0 } in x` is `-0.0`.
JoL
CC BY-SA 4.0
2023-12-10T06:26:51.663Z
Kind-of scratch what I said. Now that I've just checked, the way it works is that `-0` is an `Integer`, which is [this ADT](https://www.stackage.org/haddock/lts-21.24/ghc-bignum-1.3/src/GHC.Num.Integer.html#Integer/GHC.Num.Integer.html#Integer) that can represent both positive and negative zero. That's then converted to other number types via their instance to type class `Num`, which has the function `fromInteger :: Num a => Integer -> a`. So conversion does happen, it's just that `-0` is first an integer type that's able to represent negative zero to cover this case.
JoL
CC BY-SA 4.0
2023-12-10T19:48:30.357Z
@JoL: That's not right either.  Thanks to the invariants on `Integer` (that small integers are always represented by a machine `Int#`), `Integer` cannot represent a distinct negative zero.  (Which is good, because there's no such integer!)  What's happening is that [`-` is not part of the literal in Haskell](https://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-280003.4): `-0` is desugared to `negate (fromIntegral (0 :: Integer))`, which is the same as what's happening in Swift with `-(0)`.
Antal Spector-Zabusky
CC BY-SA 4.0
2023-12-10T19:50:58.173Z
I wanted to say you could see the difference with [`-XNegativeLiterals`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/negative_literals.html), but there seems to be some special-casing for `0`: eliding a lot of noise, using `-ddump-simpl` says that `-0` becomes `GHC.Num.negate @GHC.Num.Integer.Integer GHC.Num.$fNumInteger (GHC.Num.Integer.IS 0#)` either way, but `-1` desugars like the former by default but as `GHC.Num.Integer.IS -1#` if `-XNegativeLiterals` is enabled.  I'm not sure what the exact rules are there.
Antal Spector-Zabusky
CC BY-SA 4.0
2023-12-10T21:48:12.243Z
@AntalSpector-Zabusky You know what, that makes a ton more sense, and now that I look back, I could've also realized I was wrong if I looked at `fromInteger (-0 :: Integer) :: Double` which is `0.0`.
JoL
# What are the consequences of allowing decimal-point-less number literals to be interpreted as floats?
Posted by: username_0 on 2023-12-08
In some languages, a number literal's type can be inferred from nearby expressions. For example, 1u64 + 2 might give a u64 value of 3, since the 2's type is inferred to be u64. This can similarly be done with floats/doubles. One language I've used that works like this is Rust; a number literal without a decimal point is an int, and a number literal with a decimal point is a float or double.
However, this can make working with floats a little inconvenient. E.g., in a weakly typed language, I could write an expression like (some_float + 1) / 2; however, since 1 and 2 are int literals in Rust, this gives an error:
let out0 = (some_float + 1) / 2;
                       ^ no implementation for `f64 + {integer}`

This makes it necessary to write something like (some_float + 1.) / 2.. So, my idea was to make number literals with a decimal point always act as floats/doubles, but allow decimal-point-less literals to be treated either as ints or floats.
I'm not sure if there any languages which take this approach. Are there any potential downsides or ambiguities that could arise from this?
**Tags:** syntax type-inference floating-point literals
> **Comments:**
> **username_1**: Haskell has overloaded number literals (which are a bit more general than your idea), you might want to look into that  
> **username_2**: Another option is to have different operators for integer and floating-point operations: this would inform the type checker about the expected types of the arguments. But syntactically this is probably more cumbersome than giving number literals unambiguous types. OCaml uses `+` and `+.` for example and it is not popular.  
> **username_3**: Perhaps, if your language allows, you could overload the + operator for both floats and integers to take an operand of a different type.  
> **username_4**: The meaning of the first paragraph is unclear to me. You suggest that `2`'s type is inferred from the nearby `u64` qualifier, then you say that Rust works that way, then you say (and show by example) that Rust doesn't work that way.  
> **username_0**: @benrg It does work that way for ints; `2` could be an `i32` or a `u8` or a `u64` and so on. But unless there's a `.` it won't interpret it as a float.  
> **username_0**: @benrg I am in fact talking about the Haskell way of doing it  
> **username_4**: @RydwolfPrograms Okay, I see what you mean.  
> **username_5**: XPath (version 2.0+) takes exactly this approach. 42 is an integer, 42.1 is a decimal, 42e0 is a double. Yes, there are plenty of downsides, but probably no more than any other approach. Note that this type inference is done only for literals within the XPath expression itself, not for data read from a source (XML) document, so it's entirely under the control of the XPath author. Note that XPath allows arithmetic and comparison operators on mixed numeric types; that's convenient but carries its own set of problems, such as transitivity.  
> **username_6**: I believe Go does something similar to this  
---
## 6 answers
### Answer by username_17 on 2023-12-09. Score: 15
## Negative zero

This *barely* feels like an answer, but it's the biggest "wut" I've noticed with it so far in Swift.

In Swift, if there is no separation between a unary minus and the number, and no method calls on the literal, the minus sign is interpreted as part of the literal. So, `let x: Double = -0` is interpreted as converting the integer -0 (i.e. 0) to a Double, giving 0.0, while `let y: Double = -(0)` is interpreted as converting the integer 0 to a Double and then negating it, giving -0.0. Writing `-0.0` avoids this by always being negative zero.
> **Comments:**
> **username_19** (2023-12-10): Haskell allows decimal-point-less number literals to be interpreted as floats, but it doesn't have this problem because it doesn't first treat the literal as an integer to later convert. Rather, it understands the type as Double from the start by using the surrounding context: `let { x :: Double; x = -0 } in x` is `-0.0`.  
> **username_19** (2023-12-10): Kind-of scratch what I said. Now that I've just checked, the way it works is that `-0` is an `Integer`, which is [this ADT](https://www.stackage.org/haddock/lts-21.24/ghc-bignum-1.3/src/GHC.Num.Integer.html#Integer/GHC.Num.Integer.html#Integer) that can represent both positive and negative zero. That's then converted to other number types via their instance to type class `Num`, which has the function `fromInteger :: Num a => Integer -> a`. So conversion does happen, it's just that `-0` is first an integer type that's able to represent negative zero to cover this case.  
> **username_20** (2023-12-10): @JoL: That's not right either.  Thanks to the invariants on `Integer` (that small integers are always represented by a machine `Int#`), `Integer` cannot represent a distinct negative zero.  (Which is good, because there's no such integer!)  What's happening is that [`-` is not part of the literal in Haskell](https://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-280003.4): `-0` is desugared to `negate (fromIntegral (0 :: Integer))`, which is the same as what's happening in Swift with `-(0)`.  
> **username_20** (2023-12-10): I wanted to say you could see the difference with [`-XNegativeLiterals`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/negative_literals.html), but there seems to be some special-casing for `0`: eliding a lot of noise, using `-ddump-simpl` says that `-0` becomes `GHC.Num.negate @GHC.Num.Integer.Integer GHC.Num.$fNumInteger (GHC.Num.Integer.IS 0#)` either way, but `-1` desugars like the former by default but as `GHC.Num.Integer.IS -1#` if `-XNegativeLiterals` is enabled.  I'm not sure what the exact rules are there.  
> **username_19** (2023-12-10): @AntalSpector-Zabusky You know what, that makes a ton more sense, and now that I look back, I could've also realized I was wrong if I looked at `fromInteger (-0 :: Integer) :: Double` which is `0.0`.  
---
### Answer by username_7 on 2023-12-08. Score: 4
### Inexact representation

`1` is `1` in `u64`, `u32`, `u16` and even `u1`. That is, they compare equals in all these types.

But floats don't have this property. `0.1f16` is represented very differently in memory than `0.1f32` or `0.1f64`, and almost certainly does *not* compare equals in any of these cases. This is the principal reason that a lot of languages don't treat them as the same as in the integral types.

### Language ergonomics

But your example of `(some_float + 1) / 2`, 1 and 2 almost certainly *have exact representations* in `some_float`'s format, and if the language detects that, it can overcome the very reason the code is rejected in the first place.

Detecting if a particular `int` has exact representation in some float format normally goes over what is expected of the parser/compiler of the language, but this question is about alternatives.

### Accept exact representations

Instead of rejecting `(some_float some_operation some_int)` in all cases, at parser level, run an extra step of detecting if the `some_int` have exact representation on `some_float` format, and accept that.

Or, in other cases, reject with a much more explanatory error diagnostic. Something like `some_int has no exact representation in f64`.

---

Note that placing a `.` on integer literals or operators to "resolve" this compilation error is a bit misleading, as there is no guarantee that any float literal will have an exact representation, in any floating point format. It only directs the compiler to call some float parsing code. Having the compiler reject this construct in *some* cases, with a very specific error message, could pose as an ideal starting point in educating programmers about the inexact nature of *most* float literals.
> **Comments:**
> **username_8** (2023-12-09): I don't buy the argument about inexact representation. Sure, `0.1` represents different exact values depending on the level of precision, but the question is asking about "*decimal-point-less number literals*". Surely `1f16`, `1f32`, `1f64`, `1u1`, `1u32`, `1i64` etc all represent the exact same value?  
> **username_9** (2023-12-10): @Bergi For small integral values yes, they all represent the same ‘exact’ numeric value (although the floats do so slightly differently from each other). But small integral values are never the issue with floats, large integral values are, and the definition of ‘large’ depends on your float size. For example `4000f16` is likely not the exact same number as `4000u16`, because most 16-bit float representations don’t have enough significand bits to represent the integer 4000 exactly.  
> **username_8** (2023-12-10): @AustinHemmelgarn Same issue with `4000u8` though  
> **username_10** (2023-12-11): @Bergi No. `4000u8` is an error, 4000 is not representable as an `u8`, but `4000f16` is (so far) not an error, because 4000 is (imprecisely, but validly) representable as an `f16`.  
> **username_9** (2023-12-15): @Bergi There’s a difference between being able to provide an exact representation, being able to provide an approximate representation, and not being able to provide any representation. The integer 4000 cannot be represented in a standard `u8` type (it will overflow or be truncated), but it can be approximated with most typical `f16` types even if they cannot provide an _exact_ representation.  
---
### Answer by username_11 on 2023-12-09. Score: 2
It seems to me that several ambiguities arise.

If your language has function overloading, then given `fn foo(u64);` and `fn foo(f64);` with different behaviors, which one is called by `foo(3)`?

If you have type inference, then given `let x = 3;`, what is the type of `x`?

Given `f64 y = (9223372036854775808 + 9223372036854775808) as f64;`, does `y` get assigned the value `18446744073709551616.0`, or `0.0`, or does it cause an integer overflow trap?
> **Comments:**
> **username_12** (2023-12-10): "If you have type inference, then given `let x = 3;`, what is the type of `x`?" This is actually only an issue in languages with half-baked inference. In Haskell, a local `let x = 3` causes the type of `x` to be inferred from how `x` is used, i.e. the expression `let x = 3 in x + (5 :: Int)` has `x :: Int` whereas `let x = 3 in x + (pi :: Double)` has `x :: Double`.  
---
### Answer by username_13 on 2023-12-18. Score: 0
Haskell does this. In Haskell, the type of the literal 2 is `Num a => a` which can be instantiated as Int, or Float, or some matrix type, or whatever else satisfies Num
> **Comments:**
> **username_14** (2023-12-19): As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer).  
---
### Answer by username_15 on 2023-12-09. Score: 0
There is a syntactic drawback and I created a small Tyr example explaining it [here](https://github.com/feldentm/TyrExamples/blob/master/floatLiterals/mar.tyr).
The thing is that, if you use `.` for member access, you need to choose if `0.f` is `0` `.` `f` or a single float literal with value `0`. My personal preference is to use member access with `.` and allow member access on integers. If you agree on that, you will see why `0.` should be interpreted as `0` `.` wrt. to tokenization. If you now think about something like "let's fix it later in the parser" you will run into all sorts of ambiguities in the `0.f` case.

But the question itself is valid. Hence, Tyr interprets `0f` as float32 0.
Wrt. to the question, I simply exchanged the `.` with an `f` to fix the ambiguity with member access. Actually, these ambiguities are far worse than all the implicit conversions between signed, unsigned and float.

One could also add `0d` for double, but I doubt that it is really needed because most users will be used to writing `0.0`.
> **Comments:**
> **username_16** (2023-12-09): Javascript also uses `.` for member access and allows member accesses on numbers, but syntactically the `.` is part of the number token (based on the longest-match rule). So I don't buy your argument that *"If you agree on that, you will see why `0.` should be interpreted as `0` `.`"* ─ Javascript's approach doesn't seem to cause any problems. In practice I think it's quite rare that you need to access a member of a literal, because the result will be some constant value which you could have written instead, but if you do ever need this then having to write `(0).foo` is not a dealbreaker.  
> **username_17** (2023-12-09): Or you can write `0..foo`, which is a little more confusing at first but technically shorter.  
> **username_15** (2023-12-09): I would need to have a look at how JavaScript does it and what the limitations really are. My experience with moving token distinction to the parser is that it always creates odd corner cases like no longer being able to split lines after a `.` without changing semantics. For me `(0).f` not being equal to `0.f` would be a deal breaker. The `0..f` comment is not valid as it would have the semantics of `0f.f` in contrast to the intended `(0).f`. Should I extend my answer by these two boundary conditions?  
> **username_0** (2023-12-09): @feldentm it seems a bit dramatic for something that minor to be a "deal-breaker". As Bbrk said, it's likely you'll almost never need to access a member of a constant, and when you do, wrapping it in parentheses is perfectly intuitive and works fine. Also, I'm not sure I understand what you mean about moving logic to the parser; "a `.` after a number is part of the same token" is a simple rule that works just fine right?  
> **username_15** (2023-12-10): @RydwolfPrograms your position on `.` makes sense if you consider `0..f` and `(0).f` good solutions for something like `(0).show()`. If you want it to be `0.show()` you need to do some parser and magic words logic. But, the more we discuss it, the more my position against having it is that `0.show()` yields a parse error and  `0..show()` yields `"0.0f"`.  
> **username_0** (2023-12-10): @feldentm In my opinion `0..f` should be a syntax error; I'd lex that as a float with an extraneous dot in it, personally, in the same way as `3.141.59`  
> **username_15** (2023-12-10): ok, now I would be really interested to see how your lexer work and what token stream it'd produce  
> **username_0** (2023-12-14): @feldentm It would just parse number literals as `\d+(\.\d*)?|\.\d+` (possibly with a `\w*` or something at the end for the type indicator thing), I've done this in all my languages. I'm not even sure what the alternative would be, tbh.  
---
### Answer by username_18 on 2023-12-13. Score: 0
In Swift, “3” on its own without the quotes is not a number, it is an IntegerConvertibleLiteral (or something like that). Type interference is complicated. In
let x: Int8 = 3
it becomes an Int8. In
let d: Double = 3 + 5
3 + 5 must be Double, therefore 3 and 5 must be Double, so 3 becomes a Double. 3 prefers to be an Int during type inference and 3.0 prefers to be a Double, so
let x = 3

turns 3 and x into Int since there is no stronger inference rule.
---
