Simplified Conversion of Integer Types

Something that’s still an annoyance to me in Swift is conversion between different integer types, both by size and signed vs. unsigned, as many mismatches result in errors requiring either refactoring to one common type, or boiler-plate to cast the values. This is annoying, and the end result seems to be that most people just use a regular Int for everything, including cases where negative values aren’t required or are even invalid (requiring code to check for these, or handle any faults).

This can be highlighted with the following simple example:

var a:Int64 = 12345
let b:Int16 = 123
a += b

The last line currently results in an error that the two types differ, which is really just an annoyance as there’s clearly ample room for the addition to proceed successfully. Requiring me to instead do a += Int64(b) which seems unnecessary.

I’d like to propose that Swift implicitly cast values to an integer type with a larger size (so in this case there is no error as an Int64 is larger than an Int16). However, in the opposite case an error would occur, but could be suppressed with the overflow operator (allowing simple casts to small sizes), for example:

var a:Int16 = 123
var b:Int64 = 12345
a &+= b

Lastly there’s the case of unsigned vs signed integers, in which case similar rules should apply, i.e- a UInt32 can be safely cast to an Int64, but would require the overflow operator to cast implicitly to Int32 (as it can’t represent the full range of positive values that UInt32 can). The trickier case is what to do with conversion of signed to unsigned types, since they could contain negative values that can’t be represented at any size, but I’d say allowing the developer to permit overflow in these cases is fine too.

Essentially what I want is a better system for converting between integer types, as my preference is to use the type that most efficiently stores the range of values that I require, particularly in arrays, but I keep finding myself having to add extra code around these which slows down development and makes for messier looking code, even though most of the time I’m taking a smaller type and manipulating it within a larger one.

Anyway, I’m wondering what others think? I know it may seem fairly minor, but I seem to have a tendency to work with numbers a lot, and always having to think about what I need to convert each variable to or from just slows me down.

- Haravikk

Something that’s still an annoyance to me in Swift is conversion between different integer types, both by size and signed vs. unsigned, as many mismatches result in errors requiring either refactoring to one common type, or boiler-plate to cast the values.

Yes, it is.

I’d like to propose that Swift implicitly cast values to an integer type with a larger size (so in this case there is no error as an Int64 is larger than an Int16).

This is also my desire.

DaveA and Max have a tentative plan to improve integer semantics in these phases:

1. Improve the comparison and shift operators to not require symmetry. You should be able to do “someUInt == someInt” and get a proper value comparison.

2. Improve the numerics related protocols to enable writing generic code over different width types.

3. Introduce subtyping relationships between the integers and the floats so that we can implicit promotions from smaller to wider types. This can either be done with a general language feature to introduce subtyping of structs/enums or by hacking it into the compiler, different folks have different opinions on how this will work, and it isn’t designed yet.

The first two are a strong goal for swift 3, the later is "nice to have” if it fits since it is “just” sugar and there is a lot of other stuff going on. We are also interested in introducing a proper BigInt type, but that is even “nicer to have” for Swift 3.

-Chris

···

On Jan 15, 2016, at 2:46 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

1 Like

+1 for all three, and +1 for introducing sub-typing for structs and enums, for those times when you want sub-typing *and* value semantics.

- Dave Sweeris

···

On Jan 15, 2016, at 09:51, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 15, 2016, at 2:46 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

Something that’s still an annoyance to me in Swift is conversion between different integer types, both by size and signed vs. unsigned, as many mismatches result in errors requiring either refactoring to one common type, or boiler-plate to cast the values.

Yes, it is.

I’d like to propose that Swift implicitly cast values to an integer type with a larger size (so in this case there is no error as an Int64 is larger than an Int16).

This is also my desire.

DaveA and Max have a tentative plan to improve integer semantics in these phases:

1. Improve the comparison and shift operators to not require symmetry. You should be able to do “someUInt == someInt” and get a proper value comparison.

2. Improve the numerics related protocols to enable writing generic code over different width types.

3. Introduce subtyping relationships between the integers and the floats so that we can implicit promotions from smaller to wider types. This can either be done with a general language feature to introduce subtyping of structs/enums or by hacking it into the compiler, different folks have different opinions on how this will work, and it isn’t designed yet.

The first two are a strong goal for swift 3, the later is "nice to have” if it fits since it is “just” sugar and there is a lot of other stuff going on. We are also interested in introducing a proper BigInt type, but that is even “nicer to have” for Swift 3.

-Chris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Random thoughts here:

Integer promotions are nice for arithmetic, but they can mask bugs for “reinterpret” style conversions. You don’t want an Int32 to Double/UnsafePointer reinterpretation to implicitly promote to Int64; you want it to be a compiler error. But that’s a special-case limitation that can reasonably be directly addressed on those APIs.

It's probably reasonably to have general rules that allow integer arithmetic to just promote to the wider type, but I don’t think we can do common-type promotion without special knowledge of type rank.

My understanding is that numerics people don’t love implicit promotions between floating-point types. Float promotions and conversions are comparatively expensive, and they can introduce subtle rounding bugs; the standard advice is that programmers should be writing their code to avoid them.

Integer-to-float promotions have similar performance issues as float promotions, and IntN -> FloatN is technically lossy.

John.

···

On Jan 15, 2016, at 9:51 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:
On Jan 15, 2016, at 2:46 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

Something that’s still an annoyance to me in Swift is conversion between different integer types, both by size and signed vs. unsigned, as many mismatches result in errors requiring either refactoring to one common type, or boiler-plate to cast the values.

Yes, it is.

I’d like to propose that Swift implicitly cast values to an integer type with a larger size (so in this case there is no error as an Int64 is larger than an Int16).

This is also my desire.

DaveA and Max have a tentative plan to improve integer semantics in these phases:

1. Improve the comparison and shift operators to not require symmetry. You should be able to do “someUInt == someInt” and get a proper value comparison.

2. Improve the numerics related protocols to enable writing generic code over different width types.

3. Introduce subtyping relationships between the integers and the floats so that we can implicit promotions from smaller to wider types. This can either be done with a general language feature to introduce subtyping of structs/enums or by hacking it into the compiler, different folks have different opinions on how this will work, and it isn’t designed yet.

The first two are a strong goal for swift 3, the later is "nice to have” if it fits since it is “just” sugar and there is a lot of other stuff going on. We are also interested in introducing a proper BigInt type, but that is even “nicer to have” for Swift 3.

We should discuss this in more detail when and if it comes up and fits into Swift 3.

I’m imagining a real ranking scheme for integer types, involving value preserving promotions from smaller to larger types. For floating point, it would be Float->Double->CGFloat, even though Double->CGFloat is lossy on 32-bit targets. The key to making this work great in swift is to only introduce a promotion if the destination type is already in the system, and to “punish” the promotion by adding a cost to the solution (thus preferencing cheaper solutions).

I’m aware of at least some of the reasons that numerics people dislike promotions (some of which don’t translate to Swift, like when people write “somefloat+4.0” when they should have written “somefloat+4.0f”), and I believe that this approach would solve them.

We will need specific design and iteration on this approach though (again, when there is time to do the implementation and evaluation) because at this point it is just a hand wavy idea.

-Chris

···

On Jan 19, 2016, at 6:57 PM, John McCall <rjmccall@apple.com> wrote:

The first two are a strong goal for swift 3, the later is "nice to have” if it fits since it is “just” sugar and there is a lot of other stuff going on. We are also interested in introducing a proper BigInt type, but that is even “nicer to have” for Swift 3.

Random thoughts here:

Integer promotions are nice for arithmetic, but they can mask bugs for “reinterpret” style conversions. You don’t want an Int32 to Double/UnsafePointer reinterpretation to implicitly promote to Int64; you want it to be a compiler error. But that’s a special-case limitation that can reasonably be directly addressed on those APIs.

It's probably reasonably to have general rules that allow integer arithmetic to just promote to the wider type, but I don’t think we can do common-type promotion without special knowledge of type rank.

My understanding is that numerics people don’t love implicit promotions between floating-point types. Float promotions and conversions are comparatively expensive, and they can introduce subtle rounding bugs; the standard advice is that programmers should be writing their code to avoid them.

Integer-to-float promotions have similar performance issues as float promotions, and IntN -> FloatN is technically lossy.

Same as Dave "+1 for all three, and +1 for introducing sub-typing for
structs and enums, for those times when you want sub-typing *and* value
semantics."

···

On Wednesday, 20 January 2016, Dave via swift-evolution < swift-evolution@swift.org> wrote:

+1 for all three, and +1 for introducing sub-typing for structs and enums,
for those times when you want sub-typing *and* value semantics.

- Dave Sweeris

> On Jan 15, 2016, at 09:51, Chris Lattner via swift-evolution < > swift-evolution@swift.org <javascript:;>> wrote:
>
> On Jan 15, 2016, at 2:46 AM, Haravikk via swift-evolution < > swift-evolution@swift.org <javascript:;>> wrote:
>>
>> Something that’s still an annoyance to me in Swift is conversion
between different integer types, both by size and signed vs. unsigned, as
many mismatches result in errors requiring either refactoring to one common
type, or boiler-plate to cast the values.
>
> Yes, it is.
>
>> I’d like to propose that Swift implicitly cast values to an integer
type with a larger size (so in this case there is no error as an Int64 is
larger than an Int16).
>
> This is also my desire.
>
> DaveA and Max have a tentative plan to improve integer semantics in
these phases:
>
> 1. Improve the comparison and shift operators to not require symmetry.
You should be able to do “someUInt == someInt” and get a proper value
comparison.
>
> 2. Improve the numerics related protocols to enable writing generic code
over different width types.
>
> 3. Introduce subtyping relationships between the integers and the floats
so that we can implicit promotions from smaller to wider types. This can
either be done with a general language feature to introduce subtyping of
structs/enums or by hacking it into the compiler, different folks have
different opinions on how this will work, and it isn’t designed yet.
>
> The first two are a strong goal for swift 3, the later is "nice to have”
if it fits since it is “just” sugar and there is a lot of other stuff going
on. We are also interested in introducing a proper BigInt type, but that
is even “nicer to have” for Swift 3.
>
> -Chris
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <javascript:;>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <javascript:;>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
  -- Howard.

New to forum but also +1 on all proposals. This is really a non-brainer as other languages such as Oberon/Modula-2 and their descendants have had numeric type promotion for a long time. Perhaps study how it was done there? They have the following type hierarchy: LONGREAL ⊇ REAL ⊇ LONGINT ⊇ INTEGER ⊇ SHORTINT where ⊇ means "is a superset of". So, all the larger types such as REAL includes the numbers of LONGINT, INTEGER, and SHORTINT without losing any precision (with the exception of the LONGINT to REAL where truncation might occur). A Swift type compatible hierarchy would be more complex because of the unsigned types but generally the same approach could be used Float80 ⊇ Double ⊇ Float ⊇ Int64 ⊇ Int32 ⊇ Int16 ⊇ Int8.

+1 for sub-typing structs and enums. Oh, and better numeric conversions too.

I actually like the forced cast each time and I like the nudging to use only int. So I'm against such auto conversion.

@mgriebling Welcome to the forum!

I notice you've posted to at least two threads from a very long time ago about the same topic, both to say that you're in support of the idea. It's always nice to hear from more people who are interested in improving the language, but resurrecting old threads like this is something we try to avoid because you're asking the whole community to re-read multiple old threads in order to understand what you mean by "+1", which doesn't by itself provide any new information to change the conversation.

You can use the like buttons instead for this purpose (authors do get notified about it), but since it sounds like you might have new information or ideas, feel free to start a new thread to write up those ideas; you can reference prior posts by linking to them and quickly summarizing what it is that you like about them so that others can join you in the conversation.

3 Likes