Lesson 8. Big numbers

After reading lesson 8, you’ll be able to

  • Save your zero key by specifying an exponent
  • Use Go’s big package for really big numbers
  • Use big constants and literal values

Computer programming is full of trade-offs. Floating-point types can store numbers of any size, but they lack precision and accuracy at times. Integers are accurate but have a limited range. What if you need a really big, accurate number? This lesson explores two alternatives to the native float64 and int types.

Consider this

CPUs are optimized for integer and floating-point math, but other numeric representations are possible. When you need to go big, Go has you covered.

What are some situations where integers are too small, floating-point too imprecise, or another numeric type would be more suitable?

8.1. Hitting the ceiling

If you haven’t realized it yet, 64-bit integers are mind-bogglingly big—much bigger than their 32-bit counterparts.

For some perspective, the nearest star, Alpha Centauri, is 41.3 trillion kilometers away. A trillion: that’s one followed by 12 zeros, or 1012. Rather than painstakingly typing every zero, you can write such numbers in Go with an exponent, like so:

var distance int64 = 41.3e12

An int32 or uint32 can’t contain such a large number, but an int64 doesn’t break a sweat. Now you can go about your business, perhaps calculating how many days it would take to travel to Alpha Centauri, a task tackled in the following listing.

Listing 8.1. Days to Alpha Centauri: alpha.go
const lightSpeed = 299792 // km/s
const secondsPerDay = 86400

var distance int64 = 41.3e12
fmt.Println("Alpha Centauri is", distance, "km away.")              1

days := distance / lightSpeed / secondsPerDay
fmt.Println("That is", days, "days of travel at light speed.")      2

  • 1 Prints Alpha Centauri is 41300000000000 km away
  • 2 Prints That is 1594 days of travel at light speed

As big as 64-bit integers are, there’s something bigger: space. The Andromeda Galaxy is 24 quintillion (1018) kilometers away. Even the largest unsigned integer (uint64) can only contain numbers up to 18 quintillion. Attempting to declare a variable beyond 18 quintillion reports an overflow error:

var distance uint64 = 24e18            1

  • 1 24000000000000000000 overflows uint64

But don’t panic—there are still a few options. You could use floating-point math. That’s not a bad idea, and you already know how floating-point works. But there’s another way. The next section takes a look at Go’s big package.

Note

If a variable doesn’t have an explicit type, Go will infer float64 for numbers containing exponents.

Quick check 8.1

Q1:

The distance between Mars and Earth ranges from 56,000,000 km to 401,000,000 km. Express these two values as integers with the exponent (e) syntax.

QC 8.1 answer

1:

var distance int = 56e6
distance = 401e6

 

8.2. The big package

The big package provides three types:

  • big.Int is for big integers, when 18 quintillion isn’t enough.
  • big.Float is for arbitrary-precision floating-point numbers.
  • big.Rat is for fractions like ⅓.
Note

Your code can declare new types too, but we’ll come back to that in lesson 13.

The big.Int type can happily store and operate on the distance to Andromeda Galaxy, a mere 24 quintillion kilometers.

Opting to use big.Int requires that you use it for everything in your equation, even the constants you had before. The NewInt function takes an int64 and returns a big.Int:

lightSpeed := big.NewInt(299792)
secondsPerDay := big.NewInt(86400)

NewInt isn’t going to help for a number like 24 quintillion. It won’t fit in an int64, so instead you can create a big.Int from a string:

distance := new(big.Int)
distance.SetString("24000000000000000000", 10)

After creating a new big.Int, set its value to 24 quintillion by calling the SetString method. The number 24 quintillion is in base 10 (decimal), so the second argument is 10.

Note

Methods are similar to functions. You’ll learn all about them in lesson 13. The new built-in function is for pointers, which are covered in lesson 26.

With all the values in place, the Div method performs the necessary division so the result can be displayed, as shown in the following listing.

Listing 8.2. Days to Andromeda Galaxy: andromeda.go
package main

import (
    "fmt"
    "math/big"
)

func main() {
    lightSpeed := big.NewInt(299792)
    secondsPerDay := big.NewInt(86400)

    distance := new(big.Int)
    distance.SetString("24000000000000000000", 10)
    fmt.Println("Andromeda Galaxy is", distance, "km away.")            1

    seconds := new(big.Int)
    seconds.Div(distance, lightSpeed)

    days := new(big.Int)
    days.Div(seconds, secondsPerDay)

    fmt.Println("That is", days, "days of travel at light speed.")      2
}

  • 1 Prints Andromeda Galaxy is 24000000000000000000 km away.
  • 2 Prints That is 926568346 days of travel at light speed.

As you can see, these big types are more cumbersome to work with than the native int and float64 types. They’re also slower. Those are the trade-offs for being able to accurately represent numbers of any size.

Quick check 8.2

Q1:

What are two ways to make a big.Int with the number 86,400?

QC 8.2 answer

1:

Construct a big.Int with the NewInt function:

secondsPerDay := big.NewInt(86400)

Or use the SetString method:

secondsPerDay := new(big.Int)
secondsPerDay.SetString("86400", 10)

 

8.3. Constants of unusual size

Constants can be declared with a type, just like variables. And just like variables, a uint64 constant can’t possibly contain a number like 24 quintillion:

const distance uint64 = 24000000000000000000          1

  • 1 Constant 24000000000000000000 overflows uint64

It gets interesting when you declare a constant without a type. For variables, Go uses type inference to determine the type, and in the case of 24 quintillion, overflows the int type. Constants are different. Rather than infer a type, constants can be untyped. The following line doesn’t cause an overflow error:

const distance = 24000000000000000000

Constants are declared with the const keyword, but every literal value in your program is a constant too. That means unusually sized numbers can be used directly, as shown in the following listing.

Listing 8.3. Literals of unusual size: constant.go
fmt.Println("Andromeda Galaxy is", 24000000000000000000/299792/86400, "light
            days away.")                                       1

  • 1 Prints Andromeda Galaxy is 926568346 light days away.

Calculations on constants and literals are performed during compilation rather than while the program is running. The Go compiler is written in Go. Under the hood, untyped numeric constants are backed by the big package, enabling all the usual operations with numbers well beyond 18 quintillion, as shown in the following listing.

Listing 8.4. Constants of unusual size: constant.go
const distance = 24000000000000000000
const lightSpeed = 299792
const secondsPerDay = 86400

const days = distance / lightSpeed / secondsPerDay

fmt.Println("Andromeda Galaxy is", days, "light days away.")        1

  • 1 Prints Andromeda Galaxy is 926568346 light days away.

Constant values can be assigned to variables so long as they fit. An int can’t contain 24 quintillion, but 926,568,346 fits just fine:

km := distance                                        1
days := distance / lightSpeed / secondsPerDay         2

  • 1 Constant 24000000000000000000 overflows int.
  • 2 926568346 fits into an int.

There’s a caveat to constants of unusual size. Though the Go compiler utilizes the big package for untyped numeric constants, constants and big.Int values aren’t interchangeable. Listing 8.2 displayed a big.Int containing 24 quintillion, but you can’t display the distance constant due to an overflow error:

fmt.Println("Andromeda Galaxy is", distance, "km away.")     1

  • 1 Constant 24000000000000000000 overflows int.

Very large constants are certainly useful, but they aren’t a replacement for the big package.

Quick check 8.3

Q1:

When are calculations on constants and literals performed?

QC 8.3 answer

1:

The Go compiler simplifies equations containing constants and literals during compilation.

 

Summary

  • When the native types can’t go the distance, the big package has you covered.
  • Big things are possible with constants that are untyped, and all numeric literals are untyped constants too.
  • Untyped constants must be converted to typed variables when passed to functions.

Let’s see if you got this...

Experiment: canis.go

Canis Major Dwarf is the closest known galaxy to Earth at 236,000,000,000,000,000 km from our Sun (though some dispute that it is a galaxy). Use constants to convert this distance to light years.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset