cover image for article

Golang: Math Package

Edit

Author: Meet Rajesh Gor

#go

Introduction

Moving on in the 100 days of golang series, we can take a look into the math package in golang's standard library. In programming, math is quite critical aspect, we need to perform certain mathematical operations quite regularly so golang's standard library has a package for serving some quite commonly used math functions and procedures. We'll take a look at some of the basic and common functions which are available in the math package.

Mathematical Constants

We have some constants like pi, e, Phi already defined as constants in the math package of the standard library in golang. They have a precision till 15 digits stored in float64 values.


go
package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println("Pi = ", math.Pi)
	fmt.Println("E = ", math.E)
	fmt.Println("Phi = ", math.Phi)
	fmt.Println("Sqrt of 2 = ", math.Sqrt2)
	fmt.Println("Naturla Log 2 = ", math.Ln2)
	fmt.Println("Naturla Log 10 = ", math.Ln10)
}

$ go run basic-functions/constants.go
Pi =  3.141592653589793
E =  2.718281828459045
Phi =  1.618033988749895
Sqrt of 2 =  1.4142135623730951
Naturla Log 2 =  0.6931471805599453
Naturla Log 10 =  2.302585092994046

We can use these constants in trigonometric calculations and also in scientific computing. Further, you can get a list of all constants defined in the math package of the go standard library from the documentation.

Basic Math functions

We have some quite basic and fundamental functions in the math package that can be used commonly in many programs. Let's take a look at a few of them.

- Abs :parameters (float64) , returns float64

As the name suggest, the Abs it returns the absolute result of a numbers. It takes a parameter as a float64 value and returns the absolute value of the provided number as a float64 number.


go
package main

import (
	"fmt"
	"math"
)

func main() {
	a := 45
	b := 100
	diff := a - b
	fmt.Println(diff)

	absolute_diff := math.Abs(float64(a) - float64(b))
	fmt.Println(absolute_diff)
}

$ go run basic-functions/main.go
-55
55

As, we can see the Abs function takes in a float64 value and returns a absolute value of the given number that too a float64 value. We need to cast the numbers a and b into float64 as we have not provided the initial values and so the compiler has assigned the type to them as int.

Type Casting

We can caste a type into other by using the variable around the type name as type_name(variable). In the above example we have converted the int value 45 into a float64 as float64(45) which again yields 45 but as a float64 type.


go
foo := 77
fmt.Printf("Type of foo = %T \n", foo)
fmt.Println("foo = ", int(foo))
fmt.Println("String Cast: ", string(foo))
fmt.Println("Float Cast: ", float64(foo))

Though not every type cannot be casted into due to quite oblivious reasons, for instance 77 or any other integer value (except for 0 or 1) cannot be converted into boolean value.

Hello Gopher! Just a small note, the math package almost deals with float64 types rather than int to avoid backwards compatibility to perform operations on floating point values which can be casted into integers rather than defining separate functions for decimal values and integers.

- Min/Max: parameters(float64) , returns float64

We can get the minimum and maximum value of the two numbers provided to the function.


go
var float64 a = 120
var float64 b = 54

minimum := math.Min(float64(a), float64(b))
maximum := math.Max(float64(a), float64(b))
fmt.Printf("Min of %v and %v is %v \n", a, b, minimum)
fmt.Printf("Max of %v and %v is %v \n", a, b, maximum)

$ go run basic-functions/main.go
Min of 120 and 54 is 54
Max of 120 and 54 is 120

- Pow : parameters(float64, float64) , returns float64

- Pow10: parameters(int) , returns float64

The Pow function is used to get the exponential result of the base number. So, if we provide the values x and y, we would get the result as the number x raised to y.


go
var x float64 = 3
var y float64 = 4
z := math.Pow(x, y)
z10 := math.Pow10(int(x))
fmt.Println("X ^ Y = ", z)
fmt.Println("10 ^ X = ", z10)

$ go run basic-functions/main.go
X ^ Y =  81
10 ^ X =  1000

We also have the Pow10 function which works just like the pow function except the x value is 10 and we don't have to provide it, there is just one parameter as a integer which returns a float64 value.

- Sqrt: parameters(float64) , returns float64

The Sqrt function as the name suggest, it is used to get the square root value of a floating point value which returns a float64 value.


go
var k float64 = 125
sqrt_of_k := math.Sqrt(k)
cbrt_of_k := math.Cbrt(k)

fmt.Printf("Square root of %v = %v \n", k, sqrt_of_k)
fmt.Printf("Cube root of %v = %v \n", k, cbrt_of_k)

$ go run basic-functions/main.go
Square root of 125 = 11.180339887498949
Cube root of 125 = 5

- Trunc: parameters(float64) , returns float64

The Truncate function provides the way to round off a decimal value(float64) to an integer but it returns a value in float64.


go
var p float64 = 445.235
trunc_p := math.Trunc(p)
fmt.Printf("Truncated value of %v = %v \n", p, trunc_p)
p = 123.678
trunc_p = math.Trunc(p)
fmt.Printf("Truncated value of %v = %v \n", p, trunc_p)

$ go run basic-functions/main.go
Truncated value of 445.235 = 445
Truncated value of 123.678 = 123

- Ceil : parameters(float64) , returns float64

We also can use the Ceil function to roud up the value to the next integer value but the value is returned as float64.


go
var c float64 = 33.25
ceil_c := math.Ceil(c)
fmt.Printf("Ceiled value of %v = %v \n", c, ceil_c)
c = 134.78
ceil_c = math.Ceil(c)
fmt.Printf("Ceiled value of %v = %v \n", c, ceil_c)

$ go run basic-functions/main.go
Ceiled value of 33.25 = 34
Ceiled value of 134.78 = 135

- Trigonometric Functions

Trigonometric functions are quite helpful that can help in intense mathematical computations in backend projects or precision dependent projects. We have functions Sin, Cos, SinCos, Tan, hyperbolic functions in Trigonometric functions like Sinh, Cosh, Tanh, and Inverse Trigonometric functions like Asin, Asinh, etc.


go
package main

import (
	"fmt"
	"math"
)

func main() {
	// basic trigonometric functions
	var x float64 = math.Pi / 2
	sinx := math.Sin(x)
	cosx := math.Cos(x)
	tanx := math.Tan(x)
	fmt.Printf("Sin(%v) = %v \n", x, sinx)
	fmt.Printf("Cos(%v) = %v \n", x, cosx)
	fmt.Printf("Tan(%v) = %v \n", x, tanx)

	// hyperbolic trigonometric functions
	var h float64 = math.Pi / 2
	sinh := math.Sinh(h)
	cosh := math.Cosh(h)
	tanh := math.Tanh(h)
	fmt.Printf("Sinh(%v) = %v \n", h, sinh)
	fmt.Printf("Cosh(%v) = %v \n", h, cosh)
	fmt.Printf("Tanh(%v) = %v \n", h, tanh)

	// Inverse Trigonometric functions
	var y float64 = -1
	arc_sin := math.Asin(y) // -pi/2 radians or 90 degrees
	arc_cos := math.Acos(y) // pi randians or 180 degrees
	arc_tan := math.Atan(y) 
	fmt.Printf("Sin^-1(%v) = %v \n", y, arc_sin)
	fmt.Printf("Cos^-1(%v) = %v \n", y, arc_cos)
	fmt.Printf("Tan^-1(%v) = %v \n", y, arc_tan)

$ go run basic-functions/trignometric.go
Sin(1.5707963267948966) = 1
Cos(1.5707963267948966) = 6.123233995736757e-17
Tan(1.5707963267948966) = 1.6331239353195392e+16
Sinh(1.5707963267948966) = 2.3012989023072947
Cosh(1.5707963267948966) = 2.5091784786580567
Tanh(1.5707963267948966) = 0.9171523356672744
Sin^-1(-1) = -1.5707963267948966
Cos^-1(-1) = 3.141592653589793
Tan^-1(-1) = -0.7853981633974483

Here we can see that the functions are working fine and giving a decently precise value. This might be enough for simple and smaller projects, though for higher precision and accuracy areas, other computations and programming is required to compute the values.

- Exponential and Logarithmic Functions

We also have the exponential and logarithmic functions defined in the math package to leverage computations realted to formulae that deal with logarithmic or exponential calculations.


go
package main

import (
	"fmt"
	"math"
)

func main() {
	// exponential function
	var x float64 = 2
	y := math.Exp(x)
	fmt.Println("e^x = ", y)
	var n float64 = 3.5
	y = math.Exp2(n)
	fmt.Println("2^n = ", y)

	// Logarithmic function
	y = math.Log(x)
	fmt.Println("natural log x = ", y)

	n = 128
	y = math.Log2(n)
	fmt.Println("Log2 of 100 = ", y)
}

$ go run basic-functions/expo_log.go
e^x =  7.38905609893065
2^n =  11.31370849898476
natural log x =  0.6931471805599453
Log2 of 100 =  7

Here, we have exponential functions such as e^x and 2^n which might be useful in some common programming calculations. Also the logarithmic functions like log x which is natural log of x(base e), and log2 n which is logn to the base 2.

The Random package

The random sub-package in golang provides some great tools for working with random numbers and generating them. It provides exhaustive list of functions and types that help in generating pseudo random numbers.


go
package main

import (
	"fmt"
	"math/rand"
)

func main() {
	// random integer generation
	x := rand.Int()
	fmt.Println(x)

	// random number generation till range
	for i := 0; i < 5; i++ {
		y := rand.Intn(10)
		fmt.Println(y)
	}
}

$ go run basic-functions/rand.go
5577006791947779410
7
7
9
1
8

In the above example, we have used the Int function in the random sub-package of the math package which generates a pseudo random integer of the range dependent on the system architecture generally int32 or int64. We get a huge number which is pseudo random i.e. not truly random. If you try to execute the program a couple of time, you would notice the number remains the same and we are calling it random? Well we need to dive into random numbers and seeding for a different part of the series for sure.

The Intn function also generates a pseudo random number but this time, we define the range of the upper boundary to generate them. It is not inclusive of the number provided i.e. we have provide the value 10 so the number 10 is not included in the range. It's called half open interval. It starts from 0 so the range becomes mathematically [0, n) if n is the number provided to the Intn function.

The Bits package

We also have a bit sub-package in the math package of the go standard library. This sub package is used for working around with bit manipulation and operations at the binary level. This is quite helpful in competitive programming , also in understanding the basics of data structures and fundamentals in computer science.


go
package main

import (
	"fmt"
	"math/bits"
)

func main() {
	s, c := bits.Add(0, 9, 1)
	fmt.Printf("Sum = %d \nCarry = %d \n", s, c)

	// (45) in decimal = (1 0 1 1 0 1) in binary
	var n uint = 45
	length := bits.Len(n)
	ones_in_45 := bits.OnesCount(n)
	fmt.Printf("Minimum bits required to represent 45 = %d \n", length)
	fmt.Printf("Set Bits in 45 = %d \n", ones_in_45)

}

$ go run basic-functions/bit.go
Sum = 10
Carry = 0
Minimum bits required to represent 45 = 6
Set Bits in 45 = 4

Here, in the above example, we have used the bits sub pacakge in the math package, the Add function allows us to provide the two numbers and a carry bit on which it returns two values the sum and the carry. The sum is defined as the summation of x + y + carry the two numbers and the carry bit. The carry bit needs to be either 0 or 1.

Also the value provided the function i.e. x and y need to be unsigned uint iorder to work with bits.

We also have the Len function which returns the maximum number of bits required to represent the provided unsigned integer. We have used 45 which is equivalent to 10110 and hence the function returns 6 as teh number of bits. The OnesCount Function is also similar but it returns the number of set bits(the 1 bit) in the number provided to it.

We'll see this sub package in a separate section of its own. Bits is really a great pacakge to work with bits and low level manipulation of numbers in Golang.

The Complex package

The complex subpackage is really specific to the operation to the complex numbers and its operations. Using complex numbers with basic operations and trigonometric functions are provided in the package.


go
package main

import (
	"fmt"
	"math/cmplx"
)

func main() {

	x := complex(5, 8)
	y := complex(3, 4)
	mod_x := cmplx.Abs(x)
	mod_y := cmplx.Abs(y)
	conj_x := cmplx.Conj(x)
	phase_x := cmplx.Phase(x)
	mod, phase := cmplx.Polar(x)

	fmt.Println("x = ", x)
	fmt.Println("Modulus of x = ", mod_x)
	fmt.Println("Modulus of y = ", mod_y)
	fmt.Println("Conjugate of x = ", conj_x)
	fmt.Println("Phase of x = ", phase_x)
	fmt.Printf("Polar Form : %v, %v\n", mod, phase)

}

$ go run basic-functions/complex.go
x =  (5+8i)
Modulus of x =  9.433981132056603
Modulus of y =  5
Conjugate of x =  (5-8i)
Phase of x =  1.0121970114513341
Polar Form : 9.433981132056603, 1.0121970114513341

We have used the complex function to create complex numbers. The cmplx subpackage in the math package provides many functions to play with trignometric and simple operations with complex numbers. The Abs function is used to get the modulus of the provided complex number. The modulus is calculated with sqrt(x^2 + y^2), this gives the magnitude of the complex number. Here, we get the modulus as 9.43 as sqrt(25 + 64) for the complex number 5+8i. Also, for 3+4i the modulus becomes sqrt(9+16) which turns out to be 5. The Conjugate function is used to get the conjugate of the provided complex number.

Also the phase or the Argument of the complex number can be obtained with the Phase function. The phase is caluculated by the formula tan^-1 (y/x) but the angle is returned in randians. So for x = 5+8i the argument/Phase becomes tan^-1( 8/5) which is 57.995 degrees or 1.012 radians.

We have the Polar function which gives the polar form of the complex number i.e. (modulus r, phase theta) So this function returns two values the modulus and the argument/phase of the complex number. We have already calcualted both the values but this functions gets both of them in a single function. Quite neat, we can even ignore one value after the return of the function by using the ignore operator _, phase := cmplx.Polar(5+7i) to only care and get the phase/argument of the complex number or modulus, _ := cmplx.Polar(5+7i) to get the modulus from the complex number.

So that's some basic operations on complex numbers, this might have very few use cases but it's still quite useful when needed.

That's it from this part. Reference for all the code examples and commands can be found in the 100 days of Golang GitHub repository.

Conclusion

So from this section we were able to get a bit deeper introduction to the math package in golang's standard library. We covered some few important functions and constants in the main math package along with the glimpse of other subpackages like rand, cmplx and bits. We didn't get too much in detail with those sub packages as they can be explored on a separate section of their own. Hopefully, you have got a godd overview of the math package in golang which again is really important aspect in programming.

Thank you for reading. If you have any questions or feedback, please let me know in the comments or on social handles. Happy Coding :)