Integer maths in Go using constants with exponential notation

I seem to learn more about the nuances of the Go language every other day. Sometime back, I had looked at how Go untyped constants work during maths operations with typed variables.

I just found another significant part of the spec that I had previously glossed over, this one is also about untyped constants - numeric constants in Go live in an unified space with arbitrary precision and a fungible numeric type.

So, coming from a language like Python, this might not surprise us:

$ python2
Python 2.7.15 (default, Sep 18 2018, 20:16:18)

>>> i = 2000

>>> print i / 1000, type(i / 1000)
2 <type 'int'>

>>> print i / 1e3, type(i / 1e3)
2.0 <type 'float'>

This is because the exponential format (1e3) was a float, and forced the division to be a float division.

Admittedly, Python3 corrected this specific instance by making all divisions return a double value (PEP 238), that doesn’t take away the fact that the numeric literal itself is treated like a float.

This therefore was a surprise for me when I found out that this works in Go.

i := 2000
j := 2000.0
fmt.Printf("i: %v (%T)\n", i/1e3, i/1e3)
fmt.Printf("j: %v (%T)\n", j/1e3, j/1e3)

// Prints:
//   i: 2 (int)
//   j: 2 (float64)
(Play link)

So, you can use the exponential notation for working with large numbers in Go, something I thought was a no-no earlier.

now := time.Now()
secs := now.Unix()
millis := now.UnixNano() / 1e6

fmt.Printf("Secs   since 1970: %v (%T)\n", secs, secs)
fmt.Printf("Millis since 1970: %v (%T)\n", millis, millis)

// Prints:
//    Secs   since 1970: 1538782570 (int64)
//    Millis since 1970: 1538782570453 (int64)

(Play link, note that you will find more useful values by running this on your own computer, because current time is a weird concept in the Go playground)

The Go blog post on constants has an excellent section demonstrating this:

The concept of untyped constants in Go means that all the numeric constants, whether integer, floating-point, complex, or even character values, live in a kind of unified space. It's when we bring them to the computational world of variables, assignments, and operations that the actual types matter. But as long as we stay in the world of numeric constants, we can mix and match values as we like.

All these constants have numeric value 1:

'b' - 'a'

Therefore, although they have different implicit default types, written as untyped constants they can be assigned to a variable of any integer type.

Runit, Chpst and ulimit defaults Ansible privilege escalation with expect when you don't have root shell privileges