Lecture 10

Type Conversions

Converting between data types in programming is an important operation, and it is useful to distinguish between, at least, two kinds of conversions among numeric types.

A narrowing conversion is when you convert one type to a “smaller” type. For instance, consider this code:

var x float64 = 8
fmt.Println(float32(x))  // narrowing conversion; prints 8

float32(x) is a narrowing conversion because it is taking a 64-bit value and stuffing it into a 32-bit value. So 32-bits are lost. In this case, all the lost bits are 0s, and so there is no problem, but in general converting from 64-bits to 32-bits loses information and so is unsafe.

A widening conversion is when you convert from one type to a “bigger” type. For example:

var y float32 = 8
fmt.Println(float64(y))  // widening conversion; prints 8

float64(y) is a widening conversion, because it goes from a 32-bit value to a 64-bit one. No bits are lost, and so, for floats, widening conversions don’t change the converted value and so are safe.

Type Coercion

Most programming language have special coercion rules that implicitly convert the types of data in some (but usually not all) circumstances. This is typically done as a convenience for the programmer, as constantly having to specify explicit conversions is tedious and results in code that is hard to read.

For instance, many languages will let you add an integer to a floating point number without complaint, e.g. 5 + 3.2 evaluates to 8.2.

Go has interesting approach to this problem. Consider this code:

fmt.Println(5 + 3.2)   // Go, prints 8.2

5 and 3.2 are different types, and there is no + operator that works for them. So Go automatically converts 5 to a float and returns a float result.

However, this not allowed in Go:

var x int = 5        // Go
var y float32 = 3.2
fmt.Println(x + y) // compile-time type error: x and y are different types

This fails because the type coercion rules in Go only apply to constants (like 3.2), and not to variables (like x).

Short Circuit Evaluation of Boolean Operators

An important detail in the evaluation of boolean expressions in many languages is that they used so-called short-circuit evaluation rules. For example, consider the expression (x < 0) or (x > 1). From the rules of propositional logic, we know it is true just when one, or both, of x < 0 and x > 1 are true. If we actually want to calculate this (as we do in a programming language), then there is small trick: if x < 0 is true then we know the entire expression is true. There is no need to evaluate x > 1 because whether it is true or false, the entire expression is still true.

Similarly, for the expression (x >= 0) and (x <= 1), if x >= 0 happens to be false, then there is no need to evaluate x <= 1 because, regardless of its value, the entire expression must be false.

Most modern languages use short-circuit evaluation, as it is a small and simple optimization. It also allows us to write expressions like this:

if i >= 0 and i < len(lst) and isDigit(lst[i]):   # Python
        # ...

This if-condition first tests that i is in the proper range. If it’s not, then isDigit(lst[i]) is not evaluated thanks to short-circuit evaluation. If Python always did a full evaluation of the expression, then you would get a run-time error in some cases.