Julia as a calculator

The programming language Julia (www.julialang.org) is a new language that builds on a long history of so-called dynamic scripting languages (DSLs). DSLs are widely used for exploratory work and are part of the toolbox of most all data scientists, a rapidly growing area of employment. The language Julia is reminiscent of is MATLAB, though it offers many improvements over that language in terms of ease of use and speed in some cases. (Well it should, MATLAB was started back in the 70s.) Even better, julia is an open-source project which means it is free to download and install, unlike the commercial package MATLAB.

This class will use Julia to explore calculus concepts. Unlike some other programs used with calculus (e.g., Mathematica, Maple, and Sage) Julia is not a symbolic math language. Rather, we will use a numeric approach. This gives a different viewpoint on the calculus material supplementing that of the text book. Though there are a few idiosyncrasies we will see along the way, for the most part the Julia language will be both powerful and easy to learn.

In this project we start with baby steps -- how to use julia to perform operations we can do on the calculator.

Expressions

Julia can replicate the basics of a calculator with the standard notations. The familiar binary operators are +, -, *, /, and ^. You basically type in the expression, and then press the enter or return key.

For example, to add two and two:

2 + 2
## 4

Answers are typeset with leading comment signs (##) in these notes, but that isn't how they appear at the julia command line. We use comment signs to separate the input from the output in such a way that the input commands can be easily copy and pasted into julia.

Or to convert \(70\) degrees to Celsius with the standard formula \(C=5/9(F-32)\):

(5/9)*(70 - 32)
## 21.11111111111111

Of to find a value of \(32 - 16x^2\) when \(x=1.5\):

32 - 16*(1.5)^2
## -4.0

To find the value of \(\sqrt{15}\) we can use power notation:

15^(1/2)
## 3.872983346207417

Practice

Question

Compute \(22/7\) in julia.

Question

Compute \(\sqrt{220}\) in julia.

Question

Compute \(2^8\) in julia.

Precedence

The order of operations are conventions used to decide which operation will happen first, when there is a choice of more than one. A simple example, is what is the value of \(3 \cdot 2 + 1\)?

There are two binary operations in this expressions: a multiplication and an addition. Which is done first and which second?

In some instances it doesn't matter. A case of this would be \(3 + 2 + 1\). This is because addition is associative. In the case \(3 \cdot 2 + 1\) it certainly matters.

For \(3 \cdot 2 + 1\) if we did the addition first, the expression would \(9 = 3\cdot 3\). If we did the multiplication first, the value would be \(7=6+1\). In this case, we all know that the correct answer is \(7\), as we perform multiplication before addition, or in more precise terms the precedence of multiplication is higher than that of addition.

basics of PEMDAS

The standard order of the basic mathematical operations is remembered by many students through the mnemonic PEMDAS, which can be misleading, so we spell it out here:

  • (P) First parentheses
  • (E) then exponents (or powers)
  • (MD) then multiplication or division
  • (AS) then addition or subtraction.

This has the precedence of multiplication (MD) higher than that of subtraction (AS), as just mentioned.

Applying this, if we have the mathematical expression

\[ \frac{12 - 10}{2} \]

We know that the subtraction needs to be done before the division, as this is how we interpret this form of division. How to make this happen? The precedence of parentheses is used to force the subtraction before the division, as in (12-10)/2. Without parentheses you get a different answer:

(12 - 10)/2
## 1.0
12 - 10/2
## 7.0

Parentheses are used to force lower precedence operations to happen before higher precedence ones.

Same precedence -- what to do

There is a little more to the story, as we need to understand what happens when we have more then one operation with the same level. For instance, what is \(2 - 3- 4\)? Is it \((2 - 3) - 4\) or \(2 - (3 - 4)\).

Unlike addition, subtraction is not associative so this really matters. The subtraction operator is left associative meaning the evaluation of \(2 - 3 - 4\) is done by \((2-3) - 4\). The operations are performed in a left-to-right manner. Most -- but not all operations -- are left associative, some are right associative and performed in a right-to-left manner.

right to left It is the order of which operation is done first, not reading from right to left, as one might read Arabic.

To see that julia has left associative subtraction, we can just check:

2 - 3 - 4
## -5
(2 - 3) - 4
## -5
2 - (3 - 4)
## 3

Not all operations are processed left-to-right. The power operation, ^, is right associative, as this matches the mathematical usage. For example:

4^3^2
## 262144
(4^3)^2
## 4096
4^(3^2)
## 262144

What about the case where we have different operations with the same precedence? What happens then? A simple example would be \(2 + 3 - 4\)? Is this done in a left to right manner as in:

(2 + 3) - 4
## 1

Or a right-to-left manner, as in:

2 + (3 - 4)
## 1

And the answer is left-to-right:

2 + 3 - 4
## 1

Practice

Question

Wich of the following is a valid julia expression for

\[ \frac{3 - 2}{4 - 1} \]

that uses the least number of parentheses?

Question

Wich of the following is a valid julia expression for

\[ \frac{3\cdot2}{4} \]

that uses the least number of parentheses?

Question

Wich of the following is a valid julia expression for

\[ 2^{4 - 2} \]

that uses the least number of parentheses?

Question

One of these three expressions will produce a different answer, select that one:

Question

One of these three expressions will produce a different answer, select that one:

Question Unary operator: the minus sign

One of these three expressions will produce a different answer, select that one:

Question

Compute the value of

\[ \frac{9 - 5 \cdot (3-4)}{6 - 2}. \]

Question

Compute the following using julia:

\[ \frac{(.25 - .2)^2}{(1/4)^2 + (1/3)^2} \]

Question

Compute the decimal representation of the following using julia:

\[ 1 + \frac{1}{2} + \frac{1}{2^2} + \frac{1}{2^3} + \frac{1}{2^4} \]

Question

Compute the following using julia:

\[ \frac{3 - 2^2}{4 - 2\cdot3} \]

Question

Compute the following using julia:

\[ (1/2) \cdot 32 \cdot 3^2 + 100 \cdot 3 - 20 \]

Using functions

Most all calculators used are not limited to these basic arithmetic operations. So-called scientific calculators provide buttons for many of the common mathematical functions, such as exponential, logs, and trigonometric functions. Julia provides these too, of course.

There are special functions to perform common powers. For example, the square-root function is used as:

sqrt(15)
## 3.872983346207417

This shows how to evaluate a function -- using its name and parentheses, as in function_name(arguments). Parentheses are also used to group expressions, as would be done to do this using the power notation:

15^(1/2)
## 3.872983346207417

Additionally, parentheses are also used to make "tuples", a concept we don't pursue here but that is important for programming with julia. The point here is the context of how parentheses are used is important, though for the most part the usage is the same as their dual use in your calculus text.

There is also a cube-root function:

cbrt(27)
## 3.0

These two functions are not exactly the same as using ^, as they differ when the inputs are not in their domain:

sqrt(-1)
## "DomainError()"
(-1)^(1/2)          ## NaN is "not a number"
## NaN

Here both give no answer -- though differently, as opposed to the value of imaginary value \(i\) (or im in julia). For cube roots, the difference is different:

cbrt(-8)            ## correct
## -2.0
(-8)^(1/3)              ## need first parentheses, why?
## NaN

[ The reason for the issues with ^ is due to input being real, but the definition wants to return a complex value. Try: (-8 + 0im)^(1/3). ]

trigonometric functions

The basic trigonometric functions in julia work with radians:

sin(pi/4)
## 0.7071067811865475
cos(pi/3)
## 0.5000000000000001

But students think in degrees. What to do? Well, you can always convert via the ratio \(\pi/180\):

sin(45 * pi/180)
## 0.7071067811865475
cos(60 * pi/180)
## 0.5000000000000001

However, julia provides the student-friendly functions sind, cosd, and tand to work directly with degrees:

sind(45)
## 0.7071067811865476
cosd(45)
## 0.7071067811865476

Exponential and logs

The values \(e^x\) can be done with the built-in constant e:

e^2
## 7.38905609893065

Or through the function exp(x):

exp(2)
## 7.38905609893065

As, e can be redefined, it is best to use the latter style, though it takes a bit more typing.

The logarithm function, log does log base \(e\):

log(exp(2))
## 2.0

To do base 10, one can specify it as the first argument:

log(10, exp(2))
## 0.8685889638065035

Or use the function log10.

some useful functions

There are some other useful functions For example, abs for the absolute value, round for rounding, floor for rounding down and ceil for rounding up. Here are some examples

round(3.14)
## 3.0
floor(3.14)
## 3.0
ceil(3.14)
## 4.0

The observant eye will notice the answers above are not integers. (How to tell?) What to do if you want an integer? These functions have versions iround, ifloor, and iceil to return integer values. The thinking here is that functions should generally return values in the type of the variable input and when these are given floating point values, they return floating point values. (In general, floating point values can be converted to integers through integer.)

Practice

Question

What is the value of \(\sin(\pi/10)\)?

Question

What is the value of \(\sin(52^\circ)\)?

Question

Is \(\sin^{-1}(\sin(3\pi/2))\) equal to \(3\pi/2\)?

Question

What is the value of round(3.5000)

Question

What is the value of sqrt(32 - 12)

Question

Which is greater \(e^\pi\) or \(\pi^e\)?

Question

What is the value of \(\pi - (x - \sin(x)/\cos(x))\) when \(x=3\)?

Question

Search the page mathematical functions for a function which finds the factorial of n. The proper julia command to find \(10!\) would be:

Variables

With a calculator, one can store values into a memory for later usage. This useful feature with calculators is greatly enhanced with computer languages, where one can bind, or assign, a variable to a value. For example the command x=2 will bind x to the value \(2\):

x = 2
## 2

So, when we evaluate

x^2
## 4

The value assigned to x is looked up and used to return \(4\).

The word "dynamic" to describe the Julia language refers to the fact that variables can be reassigned and retyped. For example:

x = sqrt(2) ## a Float64 now
## 1.4142135623730951

In julia one can have single letter names, or much longer ones, such as

some_ridiculously_long_name = 3
## 3
some_ridiculously_long_name^2
## 9

The basic tradeoff being: longer names are usually more expressive and easier to remember, whereas short names are simpler to type. To get a list of the currently bound names, the whos function may be called. Not all names are syntactically valid, for example names can't begin with a number or include spaces.

To work with computer languages, it is important to appreciate that the equals sign in the variable assignment is unlike that of mathematics, where often it is used to indicate an equation which may be solved for a value. With the following computer command the right hand expression is evaluated and that value is assigned to the variable. So,

x = 2 + 3
## 5

does not assign the expression 2 + 3 to x, but rather the evaluation of that expression, which yields 5. (This also shows that the precedence of the assignment operator is lower than addition, as addition is performed first in the absence of parentheses.)

Multiple assignments

At the prompt, a simple expression is entered and, when the return key is pressed, evaluated. At times we may want to work with multiple subexpressions. A particular case might be setting different parameters:

a=0
## 0
b=1
## 1

Can be more tersely written by separating each expression using a semicolon:

a=0; b=1;

Note that julia makes this even easier, as one can do multiple assignments via "tuple destructuring:"

a, b  = 0, 1        ## printed output is a "tuple"
## (0,1)
a + b
## 1

Finally, there are begin-end blocks to group expressions:

begin
  a=0
  b=1
end
## 1

There are many other julia idioms that begin with some keyword, like begin, and terminate with end.

Practice

Question

Let \(a=10\), \(b=2.3\), and \(c=8\). Find the value of \((a-b)/(a-c)\).

Question

What is the answer to this computation?

a = 3.2; b=2.3
a^b - b^a

Question

For longer computations, it can be convenient to do them in parts, as this makes it easier to check for mistakes. (You likely do this with your calculator.)

For example, to compute

\[ \frac{p - q}{\sqrt{p(1-p)}} \]

for \(p=0.25\) and \(q=0.2\) we might do:

p, q = 0.25, 0.2
top = p - q
bottom = sqrt(p*(1-p))
ans = top/bottom

What is the result of the above?

Numbers

Unlike a calculator, julia does not treat all numbers equally. Calculators do only floating point manipulations, whereas julia has types for many different numbers: Integer, Real, Rational, Complex, and specializations depending on the number of bits that are used, e.g., Int64 and Float64. For the most part there is no need to think about the details, as values are promoted to a common type when used together. However, there are times where one needs to be aware.

Integers and floating point numbers

In the real number system of mathematics, there are the familiar real numbers and integers. The integers are viewed as a subset of the real numbers.

Julia provides types Integer and Real to represent these values. (Actually, the Integer type represents more than one actual storage type, either Int32 or Int64.) These are separate types. The type of an object is returned by typeof().

For example, the integer \(1\) is simply created by the value 1:

typeof(1)
## Int64

The floating point value \(1\) is specified by using a decimal point:

typeof(1.0)
## Float64

The two values 1 and 1.0 are not the same -- they are stored differently. However, in most -- but not all -- uses they can be used interchangeably.

When a computer is used to represent numeric values there are limitations: a computer only assigns a finite number of bits for a value. This works great in most cases, but since there are infinitely many numbers, not all possible numbers can be represented on the computer.

The first limitation is numbers can not be arbitrarily large.

Take for instance a 64-bit integer. A bit is just a place in computer memory to hold a \(0\) or a \(1\). Basically one bit is used to record the sign of the number and the remaining 63 to represent the numbers. This leaves the following range for such integers \(-2^{63}\) to \(2^{63} - 1\).

Julia is said to not provide training wheels. This means it doesn't put in checks for integer overflow, as these can slow things down. To see what happens, let just peek:

2^62                ## about 4.6 * 10^18
## 4611686018427387904
2^63                ## negative!!!
## -9223372036854775808

So if working with really large values, one must be mindful of the difference -- or your bike might crash!

Gotchas

Look at the output of

2^3^4
## 0

Why is it 0? The value of \(3^4=81\) is bigger than 63, so \(2^{81}\) will overflow.

The following works though:

2.0 ^ 3 ^ 4
## 2.4178516392292583e24

This is because the value 2.0 will use floating point arithmetic which has a much wider range of values. (The julia documentation sends you to this interesting blog post johndcook, which indicates the largest floating point value is \(2^{1023}(2 - 2^{-52})\) which is roughly 1.8e308.

Scientific notation is used to represent many numbers

A number in julia may be represented in scientific notation. The basic canonical form is \(a*10^b\), with \(-10 < a < 10\) and \(b\) is an integer. This is written in julia as aeb where e is used to separate the value from the exponent. The value 1.8e308 means \(1.8 \cdot 10^{308}\). Scientific notation makes it very easy to focus on the gross size of a number, as the exponent is set off.

The second limitation is numbers are often only an approximation.

This means expressions which are mathematically true, need not be true once approximated on the computer. For example, \(\sqrt{2}\) is an irrational number, that is, its decimal representation does not repeat the way a rational number does. Hence it is impossible to store on the computer an exact representation, at some level there is a truncation or round off. This will show up when you try something like:

2 - sqrt(2) * sqrt(2)
## -4.440892098500626e-16

That difference of basically \(10^{-16}\) is roughly the machine tolerance when representing a number. (One way to imagine this is mathematically, we have two ways to write the number \(1\):

\[ 0.\bar{9} = 0.9999... = 1 \]

but on the computer, you can't have the "..." in a decimal expansion -- it must truncate -- so instead values become something like \(0.9999999999999999\). Which are really close to \(1\) but are not precisely \(1\).

Comparing values

A typical expression in computer languages is to use == to compare the values on the left- and right-hand sides. This is not assignment, rather a question. For example:

2 == 2
## true
2 == 3
## false
sqrt(2) * sqrt(2) == 2      ## surprising?
## false

The last one would be surprising were you not paying attention to the last paragraph. Comparisons with == work well for integers and strings, but not with floating point numbers. For these the isapprox function can be used:

isapprox(sqrt(2) * sqrt(2), 2)
## true

This function basically compares whether \(|x-y| < tol\) where \(tol\) is some tolerance related to the machine precision. Machine precision is given by eps().

Comparisons do a promotion prior to comparing, so even though these numbers are of different types, the == operation treats them as equal:

1 == 1.0
## true

The === operator has an even more precise notion of equality:

1 === 1.0
## false

The special floating-point values: NaN, Inf

Floating point contains two special values: NaN and Inf to represent "not a number" and "infinity." These arise in some natural cases:

1/0             ## infinity. Also -1/0.
## Inf
0/0             ## indeterminate
## NaN

These values can come up in unexpected circumstances. For example division by \(0\) can occur due to round off errors:

x = 1e-17
## 1.0e-17
x^2/(1-cos(x))          ## should be about 2
## Inf

Rational numbers

In addition to special classes for integer and floating point values, Julia has a special class for rational numbers, or ratios of integers. To distinguish between regular division and rational numbers, julia has the // symbol to define rational numbers:

1//2
## 1//2
typeof(1//2)
## Rational{Int64}

As you know, a rational number \(m/n\) can be reduced to lowest terms by factoring out common factors. Julia does this to store its rational numbers:

2//4
## 1//2

Rational numbers are used typically to avoid round off error when using floating point values. This is easy to do, as julia will convert them when needed:

1//2 - 5//2                         ## still a rational
## -2//1
1//2 - sqrt(5)/2                    ## now a floating point
## -0.6180339887498949

However, we can't do the following, as the numerator would be non-integer when trying to make the rational number:

(1 - sqrt(5)) // 2
## "MethodError(//,(-1.2360679774997898,2))"

Scientific notation

As mentioned, one can write 3e8 for \(3 \cdot 10^8\), but in fact to julia the two values 3e8 and 3*10^8 are not quite the same, as one is stored in floating point, and one as an integer:

typeof(3e8)
## Float64
typeof(3 * 10^8)
## Int64

One can use 3.0 * 10^8 to get a floating point equivalent. (The product of the integer 10^8 is promoted to floating point when multiplied.)

Practice

question

Compute the value of \(2^{1023}(2 -2^{-52})\) using 2.0 -- not the integer 2:

question

The result of sqrt(16) is

question

The result of 16^2` is

question

The result of 1/2 is

question

The result of 2/1 is

question

Which number is 1.23e4?

question

Which number is -43e-2?

question

What is the answer to the following:

ans = round(3.4999999999999999);

question

If you need more bits, julia provides the BigInt and BigFloat classes which give \(256\) bits of precision. Using this allows one to compute \(2^3^4\) precisely as an integer:

x = BigInt(2)
ans = x^3^4

What is the answer?