A Guide to Handling Numeric Input in Go: Base Interpretation Mistakes to Avoid

A Guide to Handling Numeric Input in Go: Base Interpretation Mistakes to Avoid

·

3 min read

The Problem: Unexpected Behaviour with Leading Zeros

Recently, while working on a Go program that reverses an integer input, I encountered an unexpected issue: input values with leading zeros produced incorrect results. For example, entering 0123 returned 83 instead of 321. After some debugging, I realised the cause was rooted in Go's interpretation of numeric literals.

Here’s a simplified version of the code:

package main

import "fmt"

func main()  {
    var inp int
    fmt.Scan(&inp)
    fmt.Println(inp)  // Outputs unexpected values for leading-zero inputs
}

Entering 0123 resulted in 83, a behaviour that puzzled me at first. Why would Go interpret 0123 as 83? Let’s dive into the mechanics behind Go’s input handling to understand this better.

Insights into Numeric Literals in Go

In Go, numeric literals are interpreted based on their prefixes:

  • Decimal: Numbers without any prefix (e.g., 123) are treated as decimal.

  • Octal: Numbers starting with 0 (like 0123) are interpreted as octal (base 8).

  • Hexadecimal: Numbers prefixed with 0x or 0X (like 0x1F) are hexadecimal (base 16).

  • Binary: Numbers prefixed with 0b or 0B (like 0b1010) are binary (base 2). This was introduced in Go 1.13.

Because 0123 has a 0 prefix, Go treats it as an octal number, converting it to 83 in decimal, which explains the unexpected output.

Avoiding the Trap: Solutions for Consistent Input Interpretation

If you’re dealing with user input that might include leading zeros and you want decimal interpretation, consider reading the input as a string and then converting it explicitly to an integer. Here’s an improved version of the original code:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    var inp string
    fmt.Scan(&inp)

    num, err := strconv.Atoi(inp)
    if err != nil {
        fmt.Println("Invalid input")
        return
    }

    fmt.Println(num)  // Now it will interpret as decimal regardless of leading zeros
}

Other Situations to Watch For in Go

This behaviour isn't limited to fmt.Scan:

  • String parsing with strconv.ParseInt: Setting the base to 0 in functions like strconv.ParseInt("0123", 0, 64) will detect the base from prefixes. If consistent decimal interpretation is needed, specify 10 as the base instead.

  • Hexadecimal and binary literals in code: These are helpful, but make sure they’re not used inadvertently, as 0x and 0b prefixes will trigger hexadecimal and binary parsing.

Key Takeaways

This experience taught me the importance of understanding how Go interprets numeric literals. Here’s a summary of best practices when handling numeric input in Go:

  1. Read input as strings when dealing with user data that could have ambiguous prefixes, and specify the desired base during conversion.

  2. Explicitly specify the base when parsing numeric strings with strconv functions to avoid automatic base detection.

  3. Watch out for leading zeros in integer input, especially in cases where the base might unintentionally switch to octal.

Understanding these nuances can prevent bugs and ensure that your Go programs handle numeric inputs predictably.