Skip to content
academia.sh

Lesson 05 / 18

Type Conversion

Explicit and implicit conversion, lossy narrowing, conversion errors, and input validation.

Contents

The previous lesson showed that the same operator carries a different meaning across different types. One question remains: what happens when different types meet side by side in an expression?

The answer is one of three options: the language performs the conversion on its own, it waits for the programmer to request it explicitly, or it rejects the operation. This lesson’s subject is these three behaviors and the cost of conversions.

Explicit Conversion

In explicit conversion, the programmer requests the conversion by naming the target type:

print(int("12") + 1)          # 13     — string to integer
print(float("3.5") * 2)       # 7.0    — string to real number
print(str(12) + "1")          # 121    — integer to string
print(int(3.9))               # 3      — real number to integer

The advantage of explicit conversion is that it is visible: whoever reads the code knows a conversion is happening here, and in which direction.

The fourth line calls for attention. Converting from a real number to an integer does not round, it truncates: the fractional part is discarded.

print(int(3.9), int(-3.9))    # 3 -3   — truncation toward zero
print(round(3.9))             # 4      — rounding is a separate operation
print(round(2.5), round(3.5)) # 2 4    — halves round to the nearest even number

The last line is a detail worth knowing in numeric code: half values always rounding up is not universal. A common rule is rounding halves to the nearest even number; this prevents a large number of roundings from drifting upward in total.

Implicit Conversion

In implicit conversion, the language performs the conversion on its own to make the types compatible:

print(3 + 2.5)                # 5.5    — the integer was promoted to a real number
print(type(3 + 2.5))          # <class 'float'>

The conversion here is widening: an integer can be represented within the set of real numbers. Implicit conversions in the widening direction are generally safe.

Languages diverge in how much implicit conversion they allow. Some languages allow only safe widenings; others convert almost any type into any other. In the second group, an expression such as "3" + 4 raises no error and silently produces a result — and what that result is varies by language. The convenience comes at the cost of the error going silent.

Python sits close to the first of these two extremes: it promotes between numbers, but refuses to add a string to a number and raises an error.

Lossy Conversion

Some conversions lose information. Three typical cases:

Loss of fraction. Going from a real number to an integer discards the fractional part; converting back does not give the old value.

Range overflow. A wide integer may not fit into a narrow type. In fixed-width languages the result wraps; the previous course’s lesson on overflow showed why this happens silently.

Loss of precision. Large integers can exceed the mantissa width of real-number representation:

big = 10 ** 23
print(float(big) == big)      # False
print(int(float(big)))          # 99999999999999991611392

The value has silently changed; there is no error message. The bound stated in the lesson on floating-point numbers becomes observable here.

Practical rule: conversions in the narrowing direction are written explicitly and their justification is known. In languages that allow implicit narrowing, compiler warnings are kept enabled.

Conversion Errors

Conversion is not always possible. If a string does not represent a number, the conversion raises an error:

# int("three")   -> runtime error: invalid number representation
# int("3.5")     -> runtime error: not an integer representation
print(int(float("3.5")))          # 3  — in two steps: real first, then truncation

This is the rule when working with data coming from the outside world: user input, file contents, and network responses are not converted without being validated. There are two common patterns.

The first is to attempt the conversion and catch the error:

def to_number(text: str, default: int = 0) -> int:
    """Converts text to an integer; returns the default if conversion fails."""
    try:
        return int(text)
    except ValueError:
        return default

print(to_number("12"))         # 12
print(to_number("three"))         # 0
print(to_number("three", -1))     # -1

The second is to test the format before converting:

text = "12"
if text.isdigit():
    measurement = int(text)
    print(measurement + 1)              # 13

Which of the two patterns is chosen depends on whether the error is an expected case or an exceptional one. An invalid value in user input is an expected case; a program’s own generated data being invalid is exceptional.

From Input to Number: A Complete Example

The fact that external input arrives as a string and must be converted for numeric processing brings together every part of this topic. Through the course’s shared problem:

# Input: text with one measurement per line (imagine it was read from a file)
raw_lines = ["12", "18", "7", "twenty", "25", ""]

measurements = []
skipped = 0

for line in raw_lines:
    text = line.strip()                # discard leading and trailing whitespace
    try:
        measurements.append(int(text)) # convert to a number and add if valid
    except ValueError:
        skipped += 1                   # otherwise count and move on

print(measurements)                        # [12, 18, 7, 25]
print(skipped)                             # 2
print(sum(measurements) / len(measurements))   # 15.5
print(max(measurements))                   # 25

The program contains three separate decisions, and all three rest on the concepts established in this topic: the input is cleaned (string processing), converted (explicit conversion), and invalid values are counted separately (error handling).

There is also a fourth decision, and it is easy to overlook: should invalid lines be skipped, should the program stop, or should they be filled with a default value? This is a question of the problem, not of the language. How missing values are handled in measurement data is a separate topic in the data analytics curriculum.

How the for loop works is the subject of the next topic; here it is enough to read it only as far as each line being processed in sequence.

A Silent Trap: Strings That Look Like Numbers

A value that looks numeric staying as a string leads to a situation that raises no error but gives the wrong result:

print("12" < "9")             # True   — string comparison: character by character
print(12 < 9)                 # False  — numeric comparison
print(sorted(["12", "9", "100"]))    # ['100', '12', '9']
print(sorted([12, 9, 100]))          # [9, 12, 100]

A second example of the same class is that number notation varies by region. The decimal separator is a period in some notations and a comma in others; the thousands separator varies similarly. A language’s standard conversion function usually accepts only one notation; if text coming from a user or an external source does not conform to that notation, the conversion fails, or — worse — reads only part of the number. For this reason, the notation external data arrives in must be known, and the conversion must be performed according to that notation.

Strings are compared character by character; the character "1" comes before the character "9". If measurements read from a file are sorted without being converted, the result is lexicographic order, not numeric order. This is a typical example of the logic-error class from the first lesson: the program runs, raises no error, and gives the wrong result.

Summary

  • In explicit conversion the target type is stated by the programmer; the language performs implicit conversion on its own.
  • Converting a real number to an integer does not round, it truncates; rounding is a separate operation, and the rule for half values can vary between languages.
  • Conversions in the widening direction are safe; those in the narrowing direction can lose fraction, range, or precision, and this loss is silent.
  • Converting a string to a number can fail; external input is not converted without being validated.
  • Strings that look like numbers are compared character by character, and this changes the result of sorting.

Next Step

This topic has completed values and building expressions from them. Programs do not only flow from top to bottom, however: they branch on a condition and repeat. The next topic takes up the structures that direct flow — conditional branching and loops.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close