Numerical Types in Go Programming

GoGoBeginner
Practice Now

Introduction

Welcome gophers to this new chapter. In this section, we will learn about numerical types. The content includes commonly used integer types, floating-point types, boolean types, as well as complex numbers and the literal value syntax added in version 1.13.

Knowledge Points:

  • Integer types
  • Floating-point types
  • Boolean types
  • Complex numbers
  • Literal value syntax

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL go(("`Go`")) -.-> go/BasicsGroup(["`Basics`"]) go(("`Go`")) -.-> go/FunctionsandControlFlowGroup(["`Functions and Control Flow`"]) go(("`Go`")) -.-> go/DataTypesandStructuresGroup(["`Data Types and Structures`"]) go(("`Go`")) -.-> go/ObjectOrientedProgrammingGroup(["`Object-Oriented Programming`"]) go/BasicsGroup -.-> go/variables("`Variables`") go/FunctionsandControlFlowGroup -.-> go/for("`For`") go/FunctionsandControlFlowGroup -.-> go/if_else("`If Else`") go/FunctionsandControlFlowGroup -.-> go/functions("`Functions`") go/DataTypesandStructuresGroup -.-> go/pointers("`Pointers`") go/ObjectOrientedProgrammingGroup -.-> go/struct_embedding("`Struct Embedding`") subgraph Lab Skills go/variables -.-> lab-149067{{"`Numerical Types in Go Programming`"}} go/for -.-> lab-149067{{"`Numerical Types in Go Programming`"}} go/if_else -.-> lab-149067{{"`Numerical Types in Go Programming`"}} go/functions -.-> lab-149067{{"`Numerical Types in Go Programming`"}} go/pointers -.-> lab-149067{{"`Numerical Types in Go Programming`"}} go/struct_embedding -.-> lab-149067{{"`Numerical Types in Go Programming`"}} end

Integers

Integers can be broadly divided into two categories: unsigned integers and signed integers. Signed integers are the most widely used.

Unsigned means that it can only represent non-negative numbers (0 and positive numbers), while signed numbers can represent both negative and non-negative numbers.

Unsigned integers can be divided into four sizes: 8 bits, 16 bits, 32 bits, and 64 bits, represented by uint8, uint16, uint32, and uint64, respectively. The corresponding signed integers are int8, int16, int32, and int64. The following table shows the different ranges represented by each type:

Type Description Range
uint8 8-bit unsigned int 0 to 255
int8 8-bit signed int -128 to 127
uint16 16-bit unsigned int 0 to 65535
int16 16-bit signed int -32768 to 32767
uint32 32-bit unsigned int 0 to 4294967295
int32 32-bit signed int -2147483648 to 2147483647
uint64 64-bit unsigned int 0 to 18446744073709551615
int64 64-bit signed int -9223372036854775808 to 9223372036854775807

Let's take uint8 and int8 as examples. They are both 8-bit integers and can represent 256 values. In the unsigned integer type uint8, the range it can represent is from 0 to 255, while in the signed integer type int8, the range it can represent is from -128 to 127.

In addition to the above 8 types, there are three other special integer types, uint, int, and uintptr, where uint and int may represent different ranges on different platforms, and uintptr can be used to store pointers.

Type Range
uint uint32 on 32-bit systems, uint64 on 64-bit systems
int int32 on 32-bit systems, int64 on 64-bit systems
uintptr Unsigned integer type used for storing pointers

Now, let's create a file named integer.go to demonstrate the use of integers:

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    // View the type of int in the current environment
    // Declare a as the type int
    var a int
    // Use unsafe.Sizeof() to output the memory size occupied by the type
    fmt.Printf("The type int in the current environment is %d bits\n", unsafe.Sizeof(a)*8)

    var b int8 = 125
    // Use the %d placeholder in fmt.Printf to output the value of the integer
    // Use the %T placeholder in fmt.Printf to output the type of the variable
    fmt.Printf("The value of b is %d, and the type is %T\n", b, b)

    // Integer operations
    // Declare integers c and d, and calculate their sum
    c, d := 2, 3
    fmt.Printf("c + d = %d\n", c+d)
    // 10 - 5
    fmt.Printf("10 - 5 = %d\n", 10-5)
    // 8 * 10
    fmt.Printf("8 * 10 = %d\n", 8*10)
}

After executing the program, we get the following output:

The type int in the current environment is 64 bits
The value of b is 125, and the type is int8
c + d = 5
10 - 5 = 5
8 * 10 = 80

In this file, the unsafe.Sizeof() function can be used to obtain the number of bytes occupied by the current variable type. 1 byte (byte) is equal to 8 bits, so unsafe.Sizeof()*8 can obtain the number of bits occupied by the type. From the output, we can see that the online environment is 64 bits. The actual type of int in the online environment is int64.

We can use the following command in the terminal to determine the current system architecture:

dpkg --print-architecture
amd64

Floating-Point Numbers

Floating-point numbers, also known as decimals, can be represented in Go using two floating-point types: float32 and float64. The default floating-point type is float64.

float32 and float64 represent different precisions. The default precision of float64 is higher than that of float32.

The IEEE-754 standard is the most widely used floating-point calculation standard in computers, and modern CPUs all support this standard. Like other programming languages, Go also uses IEEE-754 to store floating-point numbers.

We know that a byte in a computer can store 8 bits. float32 is a single-precision floating-point number and occupies 4 bytes, which is 32 bits. float64 is a double-precision and occupies 8 bytes, which is 64 bits.

In float32, the sign bit occupies 1 bit, the exponent occupies 8 bits, and the remaining 23 bits are used to represent the mantissa.

In float64, the sign bit also occupies 1 bit, the exponent occupies 11 bits, and the remaining 52 bits are used to represent the mantissa.

The maximum value that float32 can represent is approximately 3.4e+38 in scientific notation, and the minimum value is 1.4e-45. The maximum value that float64 can represent is approximately 1.8e+308, and the minimum value is 4.9e-324. We can see that the range of floating-point values can be from very small to very large.

We can use the constant math.MaxFloat32 to represent the maximum value in float32. Use the constant math.MaxFloat64 to represent the maximum value in float64.

Representation of Floating-Point Numbers

When outputting floating-point numbers, we can use %f placeholder of the fmt package's Printf function. Here is an example:

package main

import (
    "fmt"
    "math"
)

func main() {
    // Output without exponential form
    fmt.Printf("2.333 without exponential form: %f\n", 2.333)
    fmt.Printf("Pi without exponential form: %f\n", math.Pi)
    // Use %.2f to keep two decimal places for Pi
    fmt.Printf("Pi with two decimal places: %.2f\n", math.Pi)
    fmt.Printf("The maximum value of float32: %f\n", math.MaxFloat32)
    // Exponential form
    fmt.Printf("2.333 in exponential form: %e", 2.333)
}

Run the command and you'll see the following output.

2.333 without exponential form: 2.333000
Pi without exponential form: 3.141593
Pi with two decimal places: 3.14
The maximum value of float32: 340282346638528859811704183484516925440.000000
2.333 in exponential form: 2.333000e+00

Boolean Types

The bool type has only two possible values: true or false, with false being the default. It has the following characteristics:

  • It cannot be converted to other types, such as converting an integer to a boolean, or converting a boolean to an integer.
  • It cannot participate in arithmetic operations.

Boolean types are usually used in conjunction with relational operators, such as =, >, and <. Let's create a file named bool.go to see a demonstration:

package main

import (
    "fmt"
)

func main() {
    // Use the %t placeholder in fmt.Printf to represent a boolean value
    fmt.Printf("Is 3 equal to 2? %t\n", 3 == 2)
    fmt.Printf("Is 2 equal to 2? %t\n", 2 == 2)

    // Determine whether a and b are equal
    a, b := 1, 2
    fmt.Printf("a is %d, b is %d\n", a, b)
    if a > b {
        fmt.Println("a is greater than b")
    } else if a == b {
        fmt.Println("a is equal to b")
    } else {
        fmt.Println("b is greater than a")
    }
}

After running the program, we get the following output:

Is 3 equal to 2? false
Is 2 equal to 2? true
a is 1, b is 2
b is greater than a

In this program, we first use %t in fmt.Printf to represent a boolean value, and then use the if statement to determine the relationship between two values. In fmt.Printf, we can use the %d placeholder to represent an integer, the %f placeholder to represent a floating-point number, and the %t placeholder to represent a boolean value.

Complex Numbers

Go also has built-in complex number types, which can be divided into complex64 and complex128 types. In complex64, both the real part and the imaginary part are 32 bits, while in complex128, both the real part and the imaginary part are 64 bits. We can easily perform complex number operations in Go.

Create a file named complex.go and enter the following code:

package main

import (
    "fmt"
)

func main() {
    // Initialize complex numbers in different ways
    c1 := complex(3, 1)
    c2 := 4 + 5i

    // Complex number operations
    c3 := c1 + c2
    c4 := c1 * c2
    // Use the real() function to obtain the real part of a complex number, and the imag() function to obtain the imaginary part of a complex number
    fmt.Printf("The real part of c1 is %v, the imaginary part is %v\n", real(c1), imag(c1))
    // %v in fmt.Printf can be used to represent complex numbers
    fmt.Printf("c1 + c2 is %v\n", c3)
    fmt.Printf("c1 * c2 is %v\n", c4)
}

After running the program, we get the following output:

The real part of c1 is 3, the imaginary part is 1
c1 + c2 is (7+6i)
c1 * c2 is (7+19i)

In this program, we first use two different ways to initialize complex numbers. Then we perform operations between complex numbers using real() and imag() to obtain the real and imaginary parts of complex numbers. In fmt.Printf, we use %v to represent complex numbers and the real and imaginary parts of complex numbers. Finally, we perform operations between complex numbers.

Literal Value Syntax

In version 1.13, Go introduced the Number literals syntax. It defines the representation of numbers in different number systems. Let's take a look at its rules.

  • Binary: add 0b before the integer. For example, 0b101 is equivalent to 5 in decimal.

  • Octal: add 0o or 0O before the integer. For example, 0o11 is equivalent to 9 in decimal.

  • Hexadecimal: add 0x or 0X before the integer. For example, 0x1b is equivalent to 27 in decimal.

  • Use _ to separate digits in the integer, for example, 0b1000_0100_0010_0001 is equivalent to 0b1000010000100001. This can improve readability.

Let's demonstrate it in detail:

package main

import "fmt"

func main() {
    // Binary, add 0b at the beginning
    var a int = 0b101
    fmt.Printf("Binary a is %b, decimal is %d\n", a, a)

    // Octal, add 0o or 0O at the beginning
    var b int = 0o11
    fmt.Printf("Octal b is %o, decimal is %d\n", b, b)

    // Hexadecimal, add 0x or 0X at the beginning
    var c int = 0x1b
    fmt.Printf("Hexadecimal c is %x, decimal is %d\n", c, c)

    // Use separators
    d := 0b1000_0100_0010_0001
    e := 0b1000010000100001
    if d == e {
        fmt.Println("d is equal to e")
    }
}

After running the program, we get the following output:

Binary a is 101, decimal is 5
Octal b is 11, decimal is 9
Hexadecimal c is 1b, decimal is 27
d is equal to e

In this program, we demonstrated the declaration and output of different number systems, as well as the use of separators. In fmt.Printf, we used different placeholders to represent different number systems. For example, %b represents binary and %x represents hexadecimal. Mastering them will improve programming efficiency.

Summary

Let's review what we have learned in this section:

  • Integers can be divided into signed integers and unsigned integers
  • The default integer types int and uint have ranges that depend on the platform
  • Representation of floating-point numbers
  • How to use complex numbers
  • Introduction of literal value syntax
  • How to use different placeholders

In this section, we explained and demonstrated common integer types, floating-point types, and boolean types. We discussed their sizes, ranges, and usage. We also introduced complex numbers and literal constants. Numerical types are the cornerstone of Go programs, and it is important for learners to study them well. In the next section, we will learn about strings and constants.

Other Go Tutorials you may like