Does it sink or float?
Bedevere: Does wood sink in water?
Villager: No, it floats! It floats!
Bedevere: What also floats in water?
Arthur: A duck!
Bedevere: Exactly. So, logically…
Villager: If… she… weighs… the same as a duck… she’s made of wood.
Bedevere: And therefore?
Villager: A witch!
— Monty Python and the Holy Grail, 1975
We discuss floating-point numbers with emphasis on the data type Double
. The section Floating-point numbers briefly presents basic arithmetic operations on floating-pointing numbers. Building on from the section Printing numbers, in the section Parsing numbers you will learn how to read a number from the command line. The section Convert from integers discusses how to convert an integer to a floating-point number, whereas the section Convert from floating-points presents subtle issues you would encounter when converting a floating-point number to an integer.
Floating-point numbers
Haskell uses the types Float
and Double
to represent floating-point numbers. The type Float
uses single-precision to represent floating-point numbers, while Double
uses double-precision. We will only discuss the type Double
and relegate the topic of Float
to another source.
The type Double
supports the usual arithmetic operators: +
(addition), -
(subtraction), *
(multiplication), and /
(division). Unlike the types Int
and Integer
, exponentiation for floating-point numbers is performed via the operator **
. Observe the following GHCi session.
1
2
3
4
5
6
7
8
9
10
11
12
ghci> a = 7.0 :: Double
ghci> b = 2.0 :: Double
ghci> a + b
9.0
ghci> a - b
5.0
ghci> a * b
14.0
ghci> a / b
3.5
ghci> a**b
49.0
The above GHCi session shows floating-point numbers being represented in decimal form. Haskell can also understand scientific notation in the form of the exponential (or e) notation. Refer to the session below.
1
2
3
4
5
6
7
8
ghci> 2.1 * (10**3)
2100.0
ghci> 2.1e3
2100.0
ghci> 3.54 * (10**(-4))
3.5400000000000004e-4
ghci> 3.54e-4
3.54e-4
Parsing numbers
Let’s build on what we have learnt from the section Printing numbers. In particular, we will write a program to obtain from the command line the radius of a circle. We will use the radius to calculate the area of a circle having the given radius, then print the area to the command line. Here is our program:
1
2
3
4
5
6
7
-- | Calculate the area of a circle.
main = do
putStrLn "Please enter the radius."
radius <- getLine
let r = read radius :: Double
let area = pi * (r ** 2)
putStrLn ("Area of circle: " ++ show area)
In the terminal session below, we compile the program and execute it. When prompted to enter the radius, we enter the number 2. The program uses the provided radius to calculate and print the area of a circle having the given radius.
1
2
3
4
5
6
7
$ ghc circle.hs
[1 of 2] Compiling Main ( circle.hs, circle.o )
[2 of 2] Linking circle
$ ./circle
Please enter the radius.
2
Area of circle: 12.566370614359172
The program circle.hs
uses the functions getLine
, read
, and pi
. The program also uses the operator <-
. Let’s consider each of them in turn.
- The function
getLine
, as its name suggests, reads a line from standard input. The standard input is usually the prompt at the terminal. The read line is converted to a string regardless of whether we enter a string or numeric digits at the command line. - The left-arrow operator (or keyword)
<-
is used within ado
block to bind the result of a function to a local variable. In terms of the programcircle.hs
the result of the functiongetLine
is assigned to the variableradius
. Why not use=
for assignment instead of<-
? That’s a quirk of ado
block. Let’s leave it as is instead of being distracted by a technical discussion about the distinction between=
and<-
for assignment. - The function
read
parses a string into a given data type. You must provide the target type. In the coderead radius :: Double
, the functionread
parses the stringradius
and returns the string as a floating-point number of typeDouble
. Neither the functionread
nor our program does any error checking. We assume that the variableradius
holds the string representation of a number. - The function
pi
returns the constant $\pi$ as a floating-point number.1
Convert from integers
To convert an integer to a floating-point number, use the function fromIntegral
. This function can convert from data of type Int
or Integer
. Then use the symbol ::
to get the type you want. For example, the GHCi session below converts various integers to type Double
.
1
2
3
4
5
6
7
8
9
10
11
12
ghci> a = 3 :: Int
ghci> b = 3 :: Integer
ghci> c = fromIntegral a :: Double
ghci> :type c
c :: Double
ghci> c
3.0
ghci> d = fromIntegral b :: Double
ghci> :type d
d :: Double
ghci> d
3.0
The function fromInteger
can also be used to convert an integer to another type. However, the function only works if you want to convert from data of type Integer
. It would not work on data of type Int
. Observe GHCi throwing a tantrum.
1
2
3
4
5
6
7
8
9
10
11
ghci> a = 4 :: Int
ghci> b = 4 :: Integer
ghci> fromInteger b :: Double
4.0
ghci> fromInteger a :: Double
<interactive>:4:13: error:
* Couldn't match expected type 'Integer' with actual type 'Int'
* In the first argument of 'fromInteger', namely 'a'
In the expression: fromInteger a :: Double
In an equation for 'it': it = fromInteger a :: Double
Convert from floating-points
Converting from an integer to floating-point is straightforward. The other way around is more complicated. In the conversion from a floating-point number $x$ to an integer, you must take the following issues into account:
- Do you want the smallest integer not less than $x$? Use the method
ceiling
, which implements the ceiling function. The ceiling function rounds a number up to the nearest integer. For example,ceiling 2.1
returns 3 andceiling (-2.1)
returns $-2$. - Do you want the greatest integer not larger than $x$? Use the method
floor
, which implements the floor function. The floor function rounds a number down to the nearest integer. For example,floor 2.1
returns 2 andfloor (-2.1)
returns $-3$. - Do you want to round $x$ to the nearest integer? Use the method
round
. This method uses the tie-breaking rule of rounding half to even. If the fractional part of $x$ is $0.5$, then $x$ is rounded to the nearest even integer. For this reason, the technique is also known as rounding to nearest, ties to even. For example, bothround 3.5
andround 4.5
result in 4. The expressionsround (-3.5)
andround (-4.5)
produce $-4$. - Do you want to round $x$ toward zero? Use the method
truncate
, which implements the notion of truncation. The number $x$ is rounded toward the nearest integer between $x$ and zero. If $x$ is positive, the rounding is done by means of the floor function. If $x$ is negative, rounding is done via the ceiling function. For example,truncate 2.6
results in 2 andtruncate (-2.6)
yields $-2$.
Exercises
Exercise 1. Haskell uses the operator ^
for non-negative integer exponentiation. The operator **
is reserved for exponentiation of floating-point numbers. There is a third exponentiation operator, i.e. ^^
, for numbers of type Fractional
where the exponent can be a negative integer. Why three different operators for exponentiation? Read the discussion here.
Exercise 2. Write a program that prompts for a person’s name and age. The program then greets the person and prints their age in 10 years time.
Exercise 3. The gravity of the Moon is 0.166 times that of the gravity of Earth. If a person weighs $x$ pounds on Earth, their weight on the Moon would be $0.166x$ pounds. Write a program to prompt a person for their weight in pounds. Print out the person’s weight on the Moon, in pounds as well as in kilograms.
Exercise 4. The planet Mercury takes about 88 Earth days to complete one orbit around the Sun. Given an age in Earth years, write a program to convert the age to Mercury years, rounded to the nearest year. Assume that each year on Earth has 365 days.
Exercise 5. The golden ratio is the mathematical constant defined by
\[\varphi = \frac{ 1 + \sqrt{5} }{ 2 }.\]Write a program to print the golden ratio as a floating-point number. Use the method sqrt
to calculate the square root of a number.
Exercise 6. The Planck constant is a fundamental physical constant used in the definition of the kilogram, among other applications. The constant is defined as
\[6.62607015 \times 10^{-34} \; \text{J} \cdot \text{Hz}^{-1}\]in terms of the unit “joule per hertz”. Express the Planck constant, excluding the unit of measurement, via the e notation.
Exercise 7. The method div
can be defined in terms of the operator /
for floating-point division and the method floor
. First, perform floating-point division, then take the floor of the result. Similarly, the method quot
can be defined in terms of /
and the method truncate
.2 Perform floating-point division and apply truncate
to the result. Verify the above within a GHCi session.
Exercise 8. Why are the numbers 42e-5
and -42e5
different from each other?
Exercise 9. The method mod
calculates the integer remainder when an integer is divided by another integer. The Haskell definition of mod
implements the definition of remainder as popularized by Donald Knuth.3 If $a$ and $n$ are integers with $n \neq 0$, the remainder $r_f$ of the division $a / n$ is defined as
and written as $r_f = a \bmod n$. The method rem
also calculates integer remainder. Instead of using the floor function, rem
uses truncation for rounding. The remainder $r_t$ of the division $a / n$ is defined as
Verify the result of mod
against the definition of $r_f$ for the following types of integers.
- Positive and positive.
- Positive and negative.
- Negative and positive.
- Negative and negative.
Repeat the exercise for rem
and $r_t$.
Exercise 10. A pyramid has a square base of length $\ell$. If the pyramid has height $h$, then the pyramid has a volume $V$ of
\[V = \frac{1}{3} \ell^2 h.\]The Great Pyramid of Giza has a square base of length 230.33 metres and a current height of 138.5 metres. Calculate the approximate volume of the Great Pyramid of Giza. Modify your result by taking into account that the pyramid originally had a height of 146.6 metres.
The sin of overconsumption is gluttony. Pie is an exception because $\sin(\pi) = 0$. ↩
These definitions of
div
andquot
are consistent with the specification as given in the document Haskell 2010 Language Report. ↩Donald E. Knuth. The Art of Computer Programming, 3rd edition, volume 1. Addison Wesley Longman, 1997, pp.39–40. ↩