Swift DISCUSSION

Swift will not add an Int to a Double, and UInt8(sum) crashes above 255: how do numeric conversions work?

Started by pierre Swift numeric conversioninteger overflowtruncatingIfNeededoverflow operatorsInt to Double
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

Swift will not add an Int to a Double, and UInt8(sum) crashes above 255: how do numeric conversions work?

pierre Swift Forum
#1

I am decoding sensor packets in Swift. let volts = raw * 3.3 / 4095 fails to compile with "binary operator '*' cannot be applied to operands of type 'Int' and 'Double'", and later a checksum computed as UInt8(sum) crashed at runtime when the sum exceeded 255. In C both lines work without any casts.

What are Swift's rules for mixing numeric types, and what is the right way to truncate or wrap a value on purpose, as a checksum needs?

Community replies 5

Re: Swift will not add an Int to a Double, and UInt8(sum) crashes above 255: how do numeric conversions work?

#2

Swift has no implicit conversions between numeric types, not even from a smaller integer to a larger one or from Int to Double. Every conversion is written as an initialiser call: let volts = Double(raw) * 3.3 / 4095. The literals are not the problem; a literal such as 4095 takes whatever numeric type the context needs, which is why let x = 3 + 0.5 compiles. Only values that already have a type, like your raw, must be converted.

The intent is that every place where precision or range can change is visible in the source.

Re: Swift will not add an Int to a Double, and UInt8(sum) crashes above 255: how do numeric conversions work?

#3

Integer initialisers check the range at runtime. When sum holds 300, UInt8(sum) traps because 300 does not fit in 8 bits, and the same happens for a negative value. (Written with a literal, the compiler rejects it before the program runs.) There are three explicit alternatives, each saying what you want: UInt8(truncatingIfNeeded: sum) keeps the low 8 bits and gives 44 (300 - 256), which is the C cast behaviour; UInt8(clamping: sum) saturates to 255; and UInt8(exactly: sum) returns nil so you can handle the failure.

For a modulo-256 checksum, truncatingIfNeeded is the one to use.

Re: Swift will not add an Int to a Double, and UInt8(sum) crashes above 255: how do numeric conversions work?

#4

Arithmetic is checked as well. If c is a UInt8 that holds 250 at runtime, the statement c += 10 is a trap, not a wrap to 4. The overflow operators &+, &- and &* wrap on purpose: c = c &+ 10 gives 4. An 8-bit additive checksum is then bytes.reduce(0 as UInt8) { $0 &+ $1 }, with no wider intermediate needed.

If you want to detect the overflow instead, c.addingReportingOverflow(10) returns the wrapped value together with a Boolean flag.

Re: Swift will not add an Int to a Double, and UInt8(sum) crashes above 255: how do numeric conversions work?

#5

Floating point to integer has its own rules. Int(3.9) truncates toward zero and gives 3, and Int(-3.9) gives -3. For nearest, round first: Int(x.rounded()). The conversion traps if the value is NaN, infinite or outside the integer's range, so a division that can produce NaN must be checked with x.isFinite before converting.

When assembling multi-byte fields, convert before shifting: UInt16(hi) << 8 | UInt16(lo). Shifting a UInt8 left by 8 simply gives 0, because the result is still 8 bits wide.

Re: Swift will not add an Int to a Double, and UInt8(sum) crashes above 255: how do numeric conversions work?

#6

A convention that reduces the number of conversions: use Int for counts, indices and general arithmetic, and the fixed-width types (UInt8, Int16, UInt32) only at the boundary where bytes are decoded or encoded. Decode each field once into an Int or Double, compute in those, and convert back when building the outgoing packet. Int is 64 bits on current 64-bit platforms, so intermediate sums of 8- or 16-bit samples will not overflow.

Signed fields need care: a 16-bit two's complement reading assembled as a UInt16 becomes signed with Int16(bitPattern: word), which reinterprets the bits instead of range-checking the value.

TEP COMMUNITY