<p>I can understand the reason for raising a warning or error when you try to convert a wider integer type to a narrower one, due to the loss of precision.
Some C compilers will warn about this:</p>
<pre class="lang-c prettyprint-override"><code>uint32_t x = 100000;
uint16_t y = x;
</code></pre>
<p>Swift raises an error:</p>
<pre><code>let y: UInt32 = 100000
let x: UInt16 = y // error: cannot convert value of type 'UInt32' to specified type 'UInt16'
</code></pre>
<p>Swift also prevents widening implicit conversions <strong>even though there is no possibility for overflow or loss of precision:</strong></p>
<pre><code>let y: UInt16 = 100000
let x: UInt32 = y // error: cannot convert value of type 'UInt16' to specified type 'UInt32'
</code></pre>
<p>Are there any drawbacks to implicit widening conversions? In what cases could allowing them impact program correctness?</p>

<types><integers><type-conversion><swift><type-safety>
When can widening conversions cause problems?
CC BY-SA 4.0
2024-07-20T21:36:45.653Z
Tangentially ontopic: in C++, multiplying `uint16_t`'s is usually done via implicit widening integer promotion to (signed) `int`. So multiplying `(uint16_t)65535 * (uint16_t)65535` may be a signed overflow and therefore an UB. The same story can happen even with `uint32_t` if your system has unlucky bit widths of fundamental types. (http://eel.is/c++draft/expr.arith.conv#1.5, http://eel.is/c++draft/conv.prom)
Vlad
CC BY-SA 4.0
2024-08-02T01:49:55.450Z
Not sure if it merits writing an answer, but no amount of widening can make a signed value "fit" in an unsigned storage space.
Karl Knechtel
<p>In Tyr, both implicit conversions are not allowed. There isn't even a warning, they types are just viewed as distinct. The rationale behind this was that even when working just with integers, if overflow semantics are intended behaviour, implicit widening can change the result in a sequence of operations if widening happens too early. This often happens in places, where space matters, like serialization. A somewhat artifical example is:</p>
<pre><code>r : short = x : byte + y : byte &lt;&lt; 16 | z : byte + w : byte
</code></pre>
<p>Here, it makes a difference if z + w can be larger than 255. In that case, you want the programmer to make an explicit choice.
Also, such situations are really uncommon. If you look at most Java code, you'll see lots of integers and barely any other number type.</p>
<p>The difference to what we did in the 90s is that the extra kilobytes lost by using int over short or byte for values that cannot really get large is just not important. Having simple, readable and maintainable code is much more important.</p>

CC BY-SA 4.0
2024-07-18T18:52:24.050Z
2024-07-18T18:52:57.020Z
2024-07-18T18:52:57.020Z
feldentm
CC BY-SA 4.0
2024-07-18T21:29:41.293Z
FWIW, Kotlin treats this similarly, needing explicit conversions between byte/char/short/int/long/float/double in most cases.  In my experience this is very occasionally annoying — but more often the extra safety is helpful, eliminating this whole class of subtle bugs.  (And yes: clear, simple, straightforward code is a major goal!)
gidds
<p><a href="https://langdev.stackexchange.com/a/3949/89">Eric Lippert's answer</a> mentions &quot;late widening conversions&quot; as a source of bugs, i.e. those where the implicit conversion occurs later than the programmer expects. A less common but also possible source of bugs is <em>early</em> widening conversions, like this in Java:</p>
<pre class="lang-java prettyprint-override"><code>byte x = (byte) 0xFF;
int y = x &gt;&gt;&gt; 4;
</code></pre>
<p>The programmer might reasonably expect the value of <code>y</code> to be <code>0x0F</code>, i.e. 15, since the shift is not sign-preserving. However, the value of <code>y</code> is actually <code>0x0FFFFFFF</code>, since the widening conversion from <code>byte</code> to <code>int</code> occurs before the shift, and the widening conversion <em>is</em> sign-preserving.</p>

CC BY-SA 4.0
2024-07-30T12:21:37.680Z
2024-07-30T12:21:37.680Z
kaya3
CC BY-SA 4.0
2024-07-30T18:07:49.330Z
Though do note, however, that this problem could be avoided if the wider type was unsigned, which Java unfortunately does not support.
CPlus
<p>On dynamic typed languages, widening (and narrowing) is somewhat expected to occur, as some operations can change the <em>value</em> type. It's common, for example, that exceeding some <code>INT_MAX</code> threshold changes the type from an integer type (where additions, subtractions and multiplications are always exact) to a floating point format, where, well... a lot of other things happen.</p>
<p>When talking about correctness, in the context of statically typed languages, we can <em>abandon</em> the notion of a numeric tower or progressive implicit widening, in favor of allowing only implicit <em>correct</em> operations. So we can frame the discussion not in terms of allowing or prohibiting widening/narrowing, but in terms of only allowing implicit exact operations, and requiring that all other operations be made explicit.</p>
<p>I will write some code in a hypothetical language to make the issues and possible solutions more explicit.</p>
<p>In the question, there is the example of assignment of an <code>u32</code> number in <code>u16</code>. Let's first code the inverse operation:</p>
<pre><code>type u32
    def implicit constructor (num u16) {}
</code></pre>
<p>That is, is declared possible to implicit construct a number in <code>u32</code> from a <code>u16</code>. This is intuitively correct, as <code>u32</code> number range overlaps and exceeds the <code>u16</code> number range, so let it pass. Now, let's examine the conversion from question:</p>
<pre><code>type u16
    def implicit constructor (num u32)
    {
        if ( num &lt;= u32( u16.MaxInteger ) )
        { /* number ranges overlaps, ok */ }
        else
        { /* What is correct here? */ }
</code></pre>
<p>I would reply: nothing. There is no correct <strong>implict</strong> conversion from <code>u32</code> to <code>u16</code>, as this may cause data loss. This conversion should be always <strong>explicit</strong>.</p>
<p>Correcting the code above:</p>
<pre><code>type u16
    def explicit constructor (num u32)    // Now explicit
    {
        // The programmer really wants this u32 as u16
        // Let's preserve the common bits of both (truncate high)
</code></pre>
<p>And in documentation, it's easier and unambiguous what this &quot;casting&quot; does.</p>
<p>Note that there is some ambiguity even in numbers in <strong>widening</strong>, not only in narrowing. For example, Solidify v0.8.0 made a <a href="https://docs.soliditylang.org/en/v0.8.26/080-breaking-changes.html" rel="nofollow noreferrer">breaking change in number conversion</a>:</p>
<blockquote>
<p>There are new restrictions on explicit type conversions. The conversion is only allowed when there is at most one change in sign, width or type-category (int, address, bytesNN, etc.). To perform multiple changes, use multiple conversions. [...] An example of such a disallowed conversion would be <code>uint16(int8)</code> since it changes both width (8 bits to 16 bits) and sign (signed integer to unsigned integer). In order to do the conversion, one has to go through an intermediate type. In the previous example, this would be <code>uint16(uint8(int8))</code> or <code>uint16(int16(int8))</code>. Note that the two ways to convert will produce different results e.g., for <code>-1</code>.</p>
</blockquote>
<p>So no Solidify programmer will need to answer <a href="https://godbolt.org/z/Ybn5T4o6q" rel="nofollow noreferrer">this</a>:</p>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdint.h&gt;

int main() {
    int8_t x = -1;
    printf(&quot;%d\n&quot;, (uint32_t)(int16_t)  x); // Prints -1
    printf(&quot;%d\n&quot;, (uint32_t)(uint16_t) x); // Prints 65535
    printf(&quot;%d\n&quot;, (uint32_t)           x); // Prints ... what?
}
</code></pre>
<p>After the notion of only correct operations are implicitly allowed, the other cases are now more obvious. Integer to floating point conversion should be explicit for avoiding possible inexact conversion, but operations between floats and ints can be allowed (see the Eric Lippert answer), unary and shifting operations should not changes the operated number type (see the kaya3 example), and operations between same types should not widen the types implicit (see the supercat answer).</p>
<p>These specific rules can be generalized for documentation, but I think that explicitly laying out each one of possible number operations and implicit conversion is the best documentation.</p>

CC BY-SA 4.0
2024-07-30T22:35:33.477Z
2024-07-30T22:42:51.000Z
2024-07-30T22:42:51.000Z
André L F S Bacci
CC BY-SA 4.0
2024-08-02T01:52:07.280Z
Dynamically typed languages commonly don't bother implementing more than one width of integral value in the first place. For that matter, they often have an arbitrary-precision integer as the "native" type.
Karl Knechtel
CC BY-SA 4.0
2024-08-08T10:05:08.610Z
`> It's common, for example, that exceeding some INT_MAX threshold changes the type from an integer type (where additions, subtractions and multiplications are always exact) to a floating point format`

what mainstream languages other than JavaScript do this?
John
CC BY-SA 4.0
2024-08-08T12:47:46.463Z
Bash, PHP, Lua. And basically any languages that the only numeric type is floats/doubles.
André L F S Bacci
<p>If a program performs an operation like <code>int1 = long1*long2;</code>, the operation may be seen as performing the following sequence of steps:</p>
<ol>
<li><p>Convert <code>long1</code> and <code>long2</code> into &quot;mathematical integers&quot; and multiply them.</p>
</li>
<li><p>Perform a narrowing conversion from &quot;mathematical integer&quot; to <code>long</code>.</p>
</li>
<li><p>Perform a narrowing conversion from <code>long</code> to <code>int</code>.</p>
</li>
</ol>
<p>Here, the second conversion would be invisible, but it would succeed in all cases where a conversion directly from &quot;mathematical integer&quot; to <code>int</code> would have succeeded, yielding the same value.</p>
<p>If the types were reversed: <code>long1 = int1*int2;</code>, then the sequence of steps would be:</p>
<ol>
<li><p>Convert <code>int1</code> and <code>int2</code> into &quot;mathematical integers&quot; and multiply them.</p>
</li>
<li><p>Perform a narrowing conversion from &quot;mathematical integer&quot; to <code>int</code>.</p>
</li>
<li><p>Perform a widening conversion from <code>int</code> to <code>long</code>.</p>
</li>
</ol>
<p>In the former case, someone who knew the type of <code>int1</code> would be able to see that the store would perform a narrowing conversion from any possible wider type to <code>int</code>.  In the latter case, there is an invisible conversion from &quot;mathematical integer&quot; to <code>int</code> which occurs before the widening conversion, and which could affect the result.  Some language designers want to issue diagnostics to call attention to such hidden conversions.</p>
<p>While it's possible to accomplish this by treating computations as yielding special data types which can be implicitly converted to the operand type, but cannot be implicitly converted to the same types as the operand type could, specifying things precisely is complicated.  It's simpler from a language-specification standpoint to forbid all implicit widening conversions even when they don't follow an implicit narrowing conversion, and even though that may ironically have the opposite of the desired effect.  While I don't know if any languages actually do this, a language designer could forbid explicit as well as implicit widening conversions for the aforementioned special types, requiring that the result of a computation first be explicitly converted to the narrow type.  Given the three code snippets:</p>
<pre><code>long1 = int1*int2;
long1 = (int)(int1*int2);
long1 = (long)(int1*int2);
</code></pre>
<p>I'd argue that the first leaves room for doubt as to whether the programmer might have intended to avoid having the result pass through type <code>int</code>, and the second removes any such doubt.  The third, however, is even worse than the first because the extra cast suggests that the programmer intended to achieve something other than the normal sequence of operations.</p>

CC BY-SA 4.0
2024-07-19T21:17:47.887Z
2024-07-19T21:17:47.887Z
supercat
<blockquote>
<p>In what cases could allowing them impact program correctness?</p>
</blockquote>
<p>I used to work at Coverity, which makes program analysis tools seeking to identify code containing bugs that are (1) plausibly created by real programmers trying to solve real business problems, (2) highly likely to cause end-user-impacting crashes, data loss or incorrect program behaviour, and (3) unlikely to be caught immediately by simply running the code. One such bug which we added a &quot;checker&quot; for during my time there was &quot;late widening conversion&quot;.</p>
<p>This happens surprisingly often in C#, Java and similar languages in real-world codebases:</p>
<pre><code>int x = whatever(); 
// ... later, in another part of the codebase ...
double scaled_x = x / 1000;
</code></pre>
<p>and the end user is then quite surprised when <code>x</code> is 3141 but <code>x_scaled</code> is 3.000, not 3.141. The meaning of the program is &quot;divide int x by int 1000 to produce a quotient rounded to int, then convert the rounded quotient to double&quot;. Plainly the developer intended to write the nigh-indistinguishable:</p>
<pre><code>double scaled_x = x / 1000.0;
</code></pre>
<p>which has the meaning &quot;implicitly convert <code>x</code> to double, then divide double by double, and assign the result to <code>scaled_x</code>&quot;.</p>
<p>If implicit numeric widening conversions are illegal in the first place then this bug is much less likely. In such a language the developer is encouraged to explicitly convert <code>x</code> to the wider type <em>first</em>.</p>
<p>We saw this defect pattern going from 32 bit integers to 64 bit integers as well; some operation that could overflow a 32 bit int is assigned to a 64 bit variable, but the conversion happens <em>after</em> the operation has already potentially overflowed because the developer has gotten confused about when the conversion happens.</p>

CC BY-SA 4.0
2024-07-18T06:59:26.497Z
2024-07-18T07:23:18.460Z
2024-07-18T07:23:18.460Z
Eric Lippert
CC BY-SA 4.0
2024-07-18T13:25:27.210Z
Even worse, given your example, is that some "clever" developer might have done this on purpose (e.g. just learning programming, discovers "cool" floor function), and a future developer might spot it and "fix" the problem. It cuts both ways. Definitely better to disallow implicit widening, in my opinion.
phyrfox
CC BY-SA 4.0
2024-07-18T15:05:40.280Z
@phyrfox: Disallowing implicit widening is often a bit irritating to developer.  In most scenarios, implicit widening results in a program that does what the developer wants.  Further, it feels slightly inconsistent to say, "Passing a dog to func(mammal) does not require an explicit cast but passing an Int32 to func(Int64) requires an explicit cast.  In SQL, explicit casts inside math are unreadable enough that I'll usually just move all my divisors to the right and do all my casts implicitly (sometimes casting the left-most numerator if there's overflow risk).
Brian
CC BY-SA 4.0
2024-07-18T15:31:18.503Z
You are both "right" in the sense that reasonable people can differ on this point, and that's why language design has *design* in the name.  Design is the art of finding reasonable compromises amongst incompatible principles. In the design of C# we tended to take a middle ground between avoiding irritation and preventing errors. In the design of VB, by way of comparison, we strongly tended towards "avoid irritation". Different languages are for different developer constituencies.
Eric Lippert
CC BY-SA 4.0
2024-07-18T17:36:11.457Z
@Brian My comment only refers to when implicit widening would cause unintentional or hidden data loss, not implicit widening as a whole. I would prefer a language that either (a) warns of potential data loss, (b) is intelligent enough to implicitly widen the operands based on the expected result type, or (c) has a special operator to address this (e.g. float x = 123 // 10 might indicate that loss is acceptable). As a lifelong developer, I don't want unnecessary casting either. I just think integer division safety would be a nice language feature.
phyrfox
CC BY-SA 4.0
2024-07-18T22:53:48.763Z
I've long thought the 'widening' notions of `double` and `float` were backward.  If someone asks what state in the US someone is from, someone who has the address and converts from "address" to "state" would discard information *that the recipient would have viewed as irrelevant".  If someone asks for a person's address and someone who just knows the state responds with a location in the middle of the State, that would likely be supplying wrong information that the recipient would probably expect to be correct.
supercat
CC BY-SA 4.0
2024-07-18T23:37:43.787Z
@supercat: The problem with that analogy is that doubling an integer [does not double its uncertainty](https://en.wikipedia.org/wiki/Interval_arithmetic#Interval_operators), so it does not make sense to analyze integer arithmetic as a special kind of interval arithmetic.
Kevin
CC BY-SA 4.0
2024-07-19T02:03:05.660Z
@Brian _"In most scenarios, implicit widening results in a program that does what the developer wants."_ That's survivor bias. When it _doesn't_ do what the developer wants, that's (a) obviously never built intentionally and (b) likely to be caught as a bug somewhere during the development track. The only teams releasing implicit conversions that _don't_ do what the developer wants are teams who don't have good test coverage and failed to notice this in whatever manual testing they will have done pre-release.
Flater
CC BY-SA 4.0
2024-07-19T02:04:57.633Z
@Brian: When mistakes are common enough to make, an "Are you sure?" confirmation prompt is warranted. Flagging implicit conversions and asking developers to do it explicitly is the equivalent of that confirmation prompt in a text-based format. It doesn't prevent doing what you're intending to do, it merely prevents _uninformedly_ doing so and causing unintended behavior.
Flater
CC BY-SA 4.0
2024-07-19T15:05:05.757Z
@Kevin: If (e.g.) 8-bit and 16-bit integers are used to represent numerical integers within a certain range, then 256 of the 16-bit equivalence classes (ECs) will map to different 8-bit equivalence classes, and the remaining 65,280 won't map to *any*.  Conversion of an 8-bit value to a 16-bit type will map to the only EC that could have mapped to the 8-bit value.  If 8-bit and 16-bit types are used to represent algebraic rings of integers congruent mod 256 or 65,536, however, then 256 ECs of the 16-bit type will map to each EC of the 8-bit type, and a "widening" conversion would have to...
supercat
CC BY-SA 4.0
2024-07-19T15:13:08.577Z
...pick a 16-bit equivalent class (typically the one in the range 0..255) which would compare unequal to many of the other equivalence classes associated with the 8-bit value.  I guess the sensible distinction would be between value-excluding conversions that perform a 1:1 mapping of *valid* values (non-modular integer types), but treat some values as invalid, from value-merging conversions which treat all values as valid but erase distinctions between equivalent classes.  Undoing the former, but not the latter, will yield the original value *in cases where the orignal value had been valid*.
supercat
CC BY-SA 4.0
2024-07-20T11:23:08.587Z
@phyrfox why "clever" and "cool" in despective quotation marks? Do you think it is better practice to do integer floor division as `int z = int(floor(double(x) / double(y)))` instead of the "cool", "clever" `int z = x / y` ?
wimi
CC BY-SA 4.0
2024-07-20T13:23:00.240Z
@wimi Of course not. The "floor" operation is explicit in `int z = x / y`; that's what integer division does. It is not explicit in `float z = x / y`. You will get two different answers depending on the data types of x and y, irrespective of z. Languages should not be ambiguous in this manner. This is where the "cool"/"clever" factor comes in. Some languages actually use `\\` (or something else) as intentional integer division, which makes the intent clear.
phyrfox
CC BY-SA 4.0
2024-07-20T18:49:14.987Z
@Brian, I find SQL is designed poorly in that respect - there are too many implicit casts, and the explicit syntax for casting is far too bulky relative to frequency of use. I think with most business arithmetic though, in any language, fixed-width fields are an inconvenience which force unnecessary sizing decisions (typically into a Procrustean bed of binary powers) and conversions.
Steve
CC BY-SA 4.0
2024-07-20T19:46:23.560Z
But in that example, the user's error wasn't an implicit conversion from a narrower int type to a wider int type, but the use of int division instead of floating-point division. You're scapegoating implicit int widening conversion for a different error.
Rosie F
CC BY-SA 4.0
2024-07-21T20:50:53.297Z
@RosieF: Correct. When we're looking for defect patterns, we almost never look for situations where a feature is *directly* misused; those are rare and not particularly interesting. Rather, we look for situations where a feature is *accidentally* misused as a consequence of a poorly understood interaction between it and a *different* feature that might also have design warts.  As programming language designers, anticipating those interactions when adding a new feature is hugely important and quite difficult.
Eric Lippert
CC BY-SA 4.0
2024-07-21T20:56:24.337Z
@RosieF: Moreover, when designing static analysis tools to find these sorts of defects, we have to think about both the "design" factors -- what are the combinations of features that lead developers to write plausible wrong programs -- but also the *fix* that we intend the developers to apply. If C# had different operators for integer vs float division, as some languages do, then we'd characterize the defect as you describe: "you used the wrong operator". But C# does not have different operators for integer and float division! We characterize the defect by its fix: you should widen earlier.
Eric Lippert
# When can widening conversions cause problems?
Posted by: username_0 on 2024-07-18
I can understand the reason for raising a warning or error when you try to convert a wider integer type to a narrower one, due to the loss of precision.
Some C compilers will warn about this:
uint32_t x = 100000;
uint16_t y = x;

Swift raises an error:
let y: UInt32 = 100000
let x: UInt16 = y // error: cannot convert value of type 'UInt32' to specified type 'UInt16'

Swift also prevents widening implicit conversions even though there is no possibility for overflow or loss of precision:
let y: UInt16 = 100000
let x: UInt32 = y // error: cannot convert value of type 'UInt16' to specified type 'UInt32'

Are there any drawbacks to implicit widening conversions? In what cases could allowing them impact program correctness?
**Tags:** types integers type-conversion swift type-safety
> **Comments:**
> **username_1**: Tangentially ontopic: in C++, multiplying `uint16_t`'s is usually done via implicit widening integer promotion to (signed) `int`. So multiplying `(uint16_t)65535 * (uint16_t)65535` may be a signed overflow and therefore an UB. The same story can happen even with `uint32_t` if your system has unlucky bit widths of fundamental types. (http://eel.is/c++draft/expr.arith.conv#1.5, http://eel.is/c++draft/conv.prom)  
> **username_2**: Not sure if it merits writing an answer, but no amount of widening can make a signed value "fit" in an unsigned storage space.  
---
## 5 answers
### Answer by username_9 on 2024-07-18. Score: 38
In what cases could allowing them impact program correctness?

I used to work at Coverity, which makes program analysis tools seeking to identify code containing bugs that are (1) plausibly created by real programmers trying to solve real business problems, (2) highly likely to cause end-user-impacting crashes, data loss or incorrect program behaviour, and (3) unlikely to be caught immediately by simply running the code. One such bug which we added a "checker" for during my time there was "late widening conversion".
This happens surprisingly often in C#, Java and similar languages in real-world codebases:
int x = whatever(); 
// ... later, in another part of the codebase ...
double scaled_x = x / 1000;

and the end user is then quite surprised when x is 3141 but x_scaled is 3.000, not 3.141. The meaning of the program is "divide int x by int 1000 to produce a quotient rounded to int, then convert the rounded quotient to double". Plainly the developer intended to write the nigh-indistinguishable:
double scaled_x = x / 1000.0;

which has the meaning "implicitly convert x to double, then divide double by double, and assign the result to scaled_x".
If implicit numeric widening conversions are illegal in the first place then this bug is much less likely. In such a language the developer is encouraged to explicitly convert x to the wider type first.
We saw this defect pattern going from 32 bit integers to 64 bit integers as well; some operation that could overflow a 32 bit int is assigned to a 64 bit variable, but the conversion happens after the operation has already potentially overflowed because the developer has gotten confused about when the conversion happens.
> **Comments:**
> **username_10** (2024-07-18): Even worse, given your example, is that some "clever" developer might have done this on purpose (e.g. just learning programming, discovers "cool" floor function), and a future developer might spot it and "fix" the problem. It cuts both ways. Definitely better to disallow implicit widening, in my opinion.  
> **username_11** (2024-07-18): @phyrfox: Disallowing implicit widening is often a bit irritating to developer.  In most scenarios, implicit widening results in a program that does what the developer wants.  Further, it feels slightly inconsistent to say, "Passing a dog to func(mammal) does not require an explicit cast but passing an Int32 to func(Int64) requires an explicit cast.  In SQL, explicit casts inside math are unreadable enough that I'll usually just move all my divisors to the right and do all my casts implicitly (sometimes casting the left-most numerator if there's overflow risk).  
> **username_9** (2024-07-18): You are both "right" in the sense that reasonable people can differ on this point, and that's why language design has *design* in the name.  Design is the art of finding reasonable compromises amongst incompatible principles. In the design of C# we tended to take a middle ground between avoiding irritation and preventing errors. In the design of VB, by way of comparison, we strongly tended towards "avoid irritation". Different languages are for different developer constituencies.  
> **username_10** (2024-07-18): @Brian My comment only refers to when implicit widening would cause unintentional or hidden data loss, not implicit widening as a whole. I would prefer a language that either (a) warns of potential data loss, (b) is intelligent enough to implicitly widen the operands based on the expected result type, or (c) has a special operator to address this (e.g. float x = 123 // 10 might indicate that loss is acceptable). As a lifelong developer, I don't want unnecessary casting either. I just think integer division safety would be a nice language feature.  
> **username_8** (2024-07-18): I've long thought the 'widening' notions of `double` and `float` were backward.  If someone asks what state in the US someone is from, someone who has the address and converts from "address" to "state" would discard information *that the recipient would have viewed as irrelevant".  If someone asks for a person's address and someone who just knows the state responds with a location in the middle of the State, that would likely be supplying wrong information that the recipient would probably expect to be correct.  
> **username_12** (2024-07-18): @supercat: The problem with that analogy is that doubling an integer [does not double its uncertainty](https://en.wikipedia.org/wiki/Interval_arithmetic#Interval_operators), so it does not make sense to analyze integer arithmetic as a special kind of interval arithmetic.  
> **username_13** (2024-07-19): @Brian _"In most scenarios, implicit widening results in a program that does what the developer wants."_ That's survivor bias. When it _doesn't_ do what the developer wants, that's (a) obviously never built intentionally and (b) likely to be caught as a bug somewhere during the development track. The only teams releasing implicit conversions that _don't_ do what the developer wants are teams who don't have good test coverage and failed to notice this in whatever manual testing they will have done pre-release.  
> **username_13** (2024-07-19): @Brian: When mistakes are common enough to make, an "Are you sure?" confirmation prompt is warranted. Flagging implicit conversions and asking developers to do it explicitly is the equivalent of that confirmation prompt in a text-based format. It doesn't prevent doing what you're intending to do, it merely prevents _uninformedly_ doing so and causing unintended behavior.  
> **username_8** (2024-07-19): @Kevin: If (e.g.) 8-bit and 16-bit integers are used to represent numerical integers within a certain range, then 256 of the 16-bit equivalence classes (ECs) will map to different 8-bit equivalence classes, and the remaining 65,280 won't map to *any*.  Conversion of an 8-bit value to a 16-bit type will map to the only EC that could have mapped to the 8-bit value.  If 8-bit and 16-bit types are used to represent algebraic rings of integers congruent mod 256 or 65,536, however, then 256 ECs of the 16-bit type will map to each EC of the 8-bit type, and a "widening" conversion would have to...  
> **username_8** (2024-07-19): ...pick a 16-bit equivalent class (typically the one in the range 0..255) which would compare unequal to many of the other equivalence classes associated with the 8-bit value.  I guess the sensible distinction would be between value-excluding conversions that perform a 1:1 mapping of *valid* values (non-modular integer types), but treat some values as invalid, from value-merging conversions which treat all values as valid but erase distinctions between equivalent classes.  Undoing the former, but not the latter, will yield the original value *in cases where the orignal value had been valid*.  
> **username_14** (2024-07-20): @phyrfox why "clever" and "cool" in despective quotation marks? Do you think it is better practice to do integer floor division as `int z = int(floor(double(x) / double(y)))` instead of the "cool", "clever" `int z = x / y` ?  
> **username_10** (2024-07-20): @wimi Of course not. The "floor" operation is explicit in `int z = x / y`; that's what integer division does. It is not explicit in `float z = x / y`. You will get two different answers depending on the data types of x and y, irrespective of z. Languages should not be ambiguous in this manner. This is where the "cool"/"clever" factor comes in. Some languages actually use `\\` (or something else) as intentional integer division, which makes the intent clear.  
> **username_15** (2024-07-20): @Brian, I find SQL is designed poorly in that respect - there are too many implicit casts, and the explicit syntax for casting is far too bulky relative to frequency of use. I think with most business arithmetic though, in any language, fixed-width fields are an inconvenience which force unnecessary sizing decisions (typically into a Procrustean bed of binary powers) and conversions.  
> **username_16** (2024-07-20): But in that example, the user's error wasn't an implicit conversion from a narrower int type to a wider int type, but the use of int division instead of floating-point division. You're scapegoating implicit int widening conversion for a different error.  
> **username_9** (2024-07-21): @RosieF: Correct. When we're looking for defect patterns, we almost never look for situations where a feature is *directly* misused; those are rare and not particularly interesting. Rather, we look for situations where a feature is *accidentally* misused as a consequence of a poorly understood interaction between it and a *different* feature that might also have design warts.  As programming language designers, anticipating those interactions when adding a new feature is hugely important and quite difficult.  
> **username_9** (2024-07-21): @RosieF: Moreover, when designing static analysis tools to find these sorts of defects, we have to think about both the "design" factors -- what are the combinations of features that lead developers to write plausible wrong programs -- but also the *fix* that we intend the developers to apply. If C# had different operators for integer vs float division, as some languages do, then we'd characterize the defect as you describe: "you used the wrong operator". But C# does not have different operators for integer and float division! We characterize the defect by its fix: you should widen earlier.  
---
### Answer by username_3 on 2024-07-18. Score: 3
In Tyr, both implicit conversions are not allowed. There isn't even a warning, they types are just viewed as distinct. The rationale behind this was that even when working just with integers, if overflow semantics are intended behaviour, implicit widening can change the result in a sequence of operations if widening happens too early. This often happens in places, where space matters, like serialization. A somewhat artifical example is:
r : short = x : byte + y : byte << 16 | z : byte + w : byte

Here, it makes a difference if z + w can be larger than 255. In that case, you want the programmer to make an explicit choice.
Also, such situations are really uncommon. If you look at most Java code, you'll see lots of integers and barely any other number type.
The difference to what we did in the 90s is that the extra kilobytes lost by using int over short or byte for values that cannot really get large is just not important. Having simple, readable and maintainable code is much more important.
> **Comments:**
> **username_4** (2024-07-18): FWIW, Kotlin treats this similarly, needing explicit conversions between byte/char/short/int/long/float/double in most cases.  In my experience this is very occasionally annoying — but more often the extra safety is helpful, eliminating this whole class of subtle bugs.  (And yes: clear, simple, straightforward code is a major goal!)  
---
### Answer by username_8 on 2024-07-19. Score: 3
If a program performs an operation like int1 = long1*long2;, the operation may be seen as performing the following sequence of steps:

Convert long1 and long2 into "mathematical integers" and multiply them.

Perform a narrowing conversion from "mathematical integer" to long.

Perform a narrowing conversion from long to int.


Here, the second conversion would be invisible, but it would succeed in all cases where a conversion directly from "mathematical integer" to int would have succeeded, yielding the same value.
If the types were reversed: long1 = int1*int2;, then the sequence of steps would be:

Convert int1 and int2 into "mathematical integers" and multiply them.

Perform a narrowing conversion from "mathematical integer" to int.

Perform a widening conversion from int to long.


In the former case, someone who knew the type of int1 would be able to see that the store would perform a narrowing conversion from any possible wider type to int.  In the latter case, there is an invisible conversion from "mathematical integer" to int which occurs before the widening conversion, and which could affect the result.  Some language designers want to issue diagnostics to call attention to such hidden conversions.
While it's possible to accomplish this by treating computations as yielding special data types which can be implicitly converted to the operand type, but cannot be implicitly converted to the same types as the operand type could, specifying things precisely is complicated.  It's simpler from a language-specification standpoint to forbid all implicit widening conversions even when they don't follow an implicit narrowing conversion, and even though that may ironically have the opposite of the desired effect.  While I don't know if any languages actually do this, a language designer could forbid explicit as well as implicit widening conversions for the aforementioned special types, requiring that the result of a computation first be explicitly converted to the narrow type.  Given the three code snippets:
long1 = int1*int2;
long1 = (int)(int1*int2);
long1 = (long)(int1*int2);

I'd argue that the first leaves room for doubt as to whether the programmer might have intended to avoid having the result pass through type int, and the second removes any such doubt.  The third, however, is even worse than the first because the extra cast suggests that the programmer intended to achieve something other than the normal sequence of operations.
---
### Answer by username_5 on 2024-07-30. Score: 2
Eric Lippert's answer mentions "late widening conversions" as a source of bugs, i.e. those where the implicit conversion occurs later than the programmer expects. A less common but also possible source of bugs is early widening conversions, like this in Java:
byte x = (byte) 0xFF;
int y = x >>> 4;

The programmer might reasonably expect the value of y to be 0x0F, i.e. 15, since the shift is not sign-preserving. However, the value of y is actually 0x0FFFFFFF, since the widening conversion from byte to int occurs before the shift, and the widening conversion is sign-preserving.
> **Comments:**
> **username_0** (2024-07-30): Though do note, however, that this problem could be avoided if the wider type was unsigned, which Java unfortunately does not support.  
---
### Answer by username_6 on 2024-07-30. Score: 1
On dynamic typed languages, widening (and narrowing) is somewhat expected to occur, as some operations can change the value type. It's common, for example, that exceeding some INT_MAX threshold changes the type from an integer type (where additions, subtractions and multiplications are always exact) to a floating point format, where, well... a lot of other things happen.
When talking about correctness, in the context of statically typed languages, we can abandon the notion of a numeric tower or progressive implicit widening, in favor of allowing only implicit correct operations. So we can frame the discussion not in terms of allowing or prohibiting widening/narrowing, but in terms of only allowing implicit exact operations, and requiring that all other operations be made explicit.
I will write some code in a hypothetical language to make the issues and possible solutions more explicit.
In the question, there is the example of assignment of an u32 number in u16. Let's first code the inverse operation:
type u32
    def implicit constructor (num u16) {}

That is, is declared possible to implicit construct a number in u32 from a u16. This is intuitively correct, as u32 number range overlaps and exceeds the u16 number range, so let it pass. Now, let's examine the conversion from question:
type u16
    def implicit constructor (num u32)
    {
        if ( num <= u32( u16.MaxInteger ) )
        { /* number ranges overlaps, ok */ }
        else
        { /* What is correct here? */ }

I would reply: nothing. There is no correct implict conversion from u32 to u16, as this may cause data loss. This conversion should be always explicit.
Correcting the code above:
type u16
    def explicit constructor (num u32)    // Now explicit
    {
        // The programmer really wants this u32 as u16
        // Let's preserve the common bits of both (truncate high)

And in documentation, it's easier and unambiguous what this "casting" does.
Note that there is some ambiguity even in numbers in widening, not only in narrowing. For example, Solidify v0.8.0 made a breaking change in number conversion:

There are new restrictions on explicit type conversions. The conversion is only allowed when there is at most one change in sign, width or type-category (int, address, bytesNN, etc.). To perform multiple changes, use multiple conversions. [...] An example of such a disallowed conversion would be uint16(int8) since it changes both width (8 bits to 16 bits) and sign (signed integer to unsigned integer). In order to do the conversion, one has to go through an intermediate type. In the previous example, this would be uint16(uint8(int8)) or uint16(int16(int8)). Note that the two ways to convert will produce different results e.g., for -1.

So no Solidify programmer will need to answer this:
#include 
#include 
#include 

int main() {
    int8_t x = -1;
    printf("%d\n", (uint32_t)(int16_t)  x); // Prints -1
    printf("%d\n", (uint32_t)(uint16_t) x); // Prints 65535
    printf("%d\n", (uint32_t)           x); // Prints ... what?
}

After the notion of only correct operations are implicitly allowed, the other cases are now more obvious. Integer to floating point conversion should be explicit for avoiding possible inexact conversion, but operations between floats and ints can be allowed (see the Eric Lippert answer), unary and shifting operations should not changes the operated number type (see the kaya3 example), and operations between same types should not widen the types implicit (see the supercat answer).
These specific rules can be generalized for documentation, but I think that explicitly laying out each one of possible number operations and implicit conversion is the best documentation.
> **Comments:**
> **username_2** (2024-08-02): Dynamically typed languages commonly don't bother implementing more than one width of integral value in the first place. For that matter, they often have an arbitrary-precision integer as the "native" type.  
> **username_7** (2024-08-08): `> It's common, for example, that exceeding some INT_MAX threshold changes the type from an integer type (where additions, subtractions and multiplications are always exact) to a floating point format`

what mainstream languages other than JavaScript do this?  
> **username_6** (2024-08-08): Bash, PHP, Lua. And basically any languages that the only numeric type is floats/doubles.  
---
