{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Chapter 5: Bits & Numbers" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "After learning about the basic building blocks of expressing and structuring the business logic in programs, we focus our attention on the **data types** Python offers us, both built-in and available via the [standard library](https://docs.python.org/3/library/index.html) or third-party packages.\n", "\n", "We start with the \"simple\" ones: Numeric types in this chapter and textual data in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_content.ipynb). An important fact that holds for all objects of these types is that they are **immutable**. To reuse the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory, implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", "\n", "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb), [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_00_content.ipynb), and [Chapter 9](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/09_arrays_00_content.ipynb) then cover the more \"complex\" data types, including, for example, the `list` type. Finally, Chapter 10 completes the picture by introducing language constructs to create custom types.\n", "\n", "We have already seen many hints indicating that numbers are not as trivial to work with as it seems at first sight:\n", "\n", "- [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#%28Data%29-Type-%2F-%22Behavior%22) reveals that numbers may come in *different* data types (i.e., `int` vs. `float` so far),\n", "- [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_content.ipynb#Boolean-Expressions) raises questions regarding the **limited precision** of `float` numbers (e.g., `42 == 42.000000000000001` evaluates to `True`), and\n", "- [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#Infinite-Recursion) shows that sometimes a `float` \"walks\" and \"quacks\" like an `int`, whereas the reverse is true in other cases.\n", "\n", "This chapter introduces all the [built-in numeric types](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): `int`, `float`, and `complex`. To mitigate the limited precision of floating-point numbers, we also look at two replacements for the `float` type in the [standard library](https://docs.python.org/3/library/index.html), namely the `Decimal` type in the [decimals](https://docs.python.org/3/library/decimal.html#decimal.Decimal) and the `Fraction` type in the [fractions](https://docs.python.org/3/library/fractions.html#fractions.Fraction) module." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## The `int` Type" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The simplest numeric type is the `int` type: It behaves like an [integer in ordinary math](https://en.wikipedia.org/wiki/Integer) (i.e., the set $\\mathbb{Z}$) and supports operators in the way we saw in the section on arithmetic operators in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#%28Arithmetic%29-Operators)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "i = 789" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Just like any other object, `789` has an identity, a type, and a value." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "140166838695792" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "id(i)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "int" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(i)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "789" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "i" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "A nice feature is using underscores `_` as (thousands) separator in numeric literals. For example, `1_000_000` evaluates to `1000000` in memory." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "1000000" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1_000_000" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Whereas mathematicians argue what the term $0^0$ means (cf., this [article](https://en.wikipedia.org/wiki/Zero_to_the_power_of_zero)), programmers are pragmatic about this and define $0^0 = 1$." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "0 ** 0" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Binary Representations" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "As computers can only store $0$s and $1$s, `int` objects are nothing but that in memory as well. Consequently, computer scientists and engineers developed conventions as to how $0$s and $1$s are \"translated\" into integers, and one such convention is the **[binary representation](https://en.wikipedia.org/wiki/Binary_number)** of **non-negative integers**. Consider the integers from $0$ through $255$ that are encoded into $0$s and $1$s with the help of this table:" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "|Bit $i$| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |\n", "|-------|-----|-----|-----|-----|-----|-----|-----|-----|\n", "| Digit |$2^7$|$2^6$|$2^5$|$2^4$|$2^3$|$2^2$|$2^1$|$2^0$|\n", "| $=$ |$128$| $64$| $32$| $16$| $8$ | $4$ | $2$ | $1$ |" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "A number consists of exactly eight $0$s and $1$s that are read from right to left and referred to as the **bits** of the number. Each bit represents a distinct multiple of $2$, the **digit**. For sure, we start counting at $0$ again.\n", "\n", "To encode the integer $3$, for example, we need to find a combination of $0$s and $1$s such that the sum of digits marked with a $1$ is equal to the number we want to encode. In the example, we set all bits to $0$ except for the first ($i=0$) and second ($i=1$) as $2^0 + 2^1 = 1 + 2 = 3$. So the binary representation of $3$ is $00~00~00~11$. To borrow some terminology from linear algebra, the $3$ is a linear combination of the digits where the coefficients are either $0$ or $1$: $3 = 0*128 + 0*64 + 0*32 + 0*16 + 0*8 + 0*4 + 1*2 + 1*1$. It is *guaranteed* that there is exactly *one* such combination for each number between $0$ and $255$.\n", "\n", "As each bit in the binary representation is one of two values, we say that this representation has a base of $2$. Often, the base is indicated with a subscript to avoid confusion. For example, we write $3_{10} = 00000011_2$ or $3_{10} = 11_2$ for short omitting leading $0$s. A subscript of $10$ implies a decimal number as we know it from grade school.\n", "\n", "We use the built-in [bin()](https://docs.python.org/3/library/functions.html#bin) function to obtain an `int` object's binary representation: It returns a `str` object starting with `\"0b\"` indicating the binary format and as many $0$s and $1$s as are necessary to encode the integer omitting leading $0$s." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'0b11'" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(3)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "We may pass a `str` object formatted this way as the argument to the [int()](https://docs.python.org/3/library/functions.html#int) built-in, together with `base=2`, to create an `int` object, for example, with the value of `3`." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "int(\"0b11\", base=2)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Moreover, we may also use the contents of the returned `str` object as a **literal** instead: Just like we type, for example, `3` without quotes (i.e., \"literally\") into a code cell to create the `int` object `3`, we may type `0b11` to obtain an `int` object with the same value." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "0b11" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Another example is the integer `177` that is the sum of $128 + 32 + 16 + 1$: Thus, its binary representation is the sequence of bits $10~11~00~01$, or to use our new notation, $177_{10} = 10110001_2$." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0b10110001'" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(177)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Analogous to typing `177` into a code cell, we may write `0b10110001`, or `0b_1011_0001` to make use of the underscores, and create an `int` object with the value `177`." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "177" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "0b_1011_0001" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`0` and `255` are the edge cases where we set all the bits to either $0$ or $1$." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0b0'" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(0)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0b1'" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(1)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0b10'" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(2)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0b11111111'" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(255)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Groups of eight bits are also called a **byte**. As a byte can only represent non-negative integers up to $255$, the table above is extended conceptually with greater digits to the left to model integers beyond $255$. The memory management needed to implement this is built into Python, and we do not need to worry about it.\n", "\n", "For example, the `789` from above is encoded with ten bits and $789_{10} = 1100010101_2$." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'0b1100010101'" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(i) # = bin(789)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "To contrast this bits encoding with the familiar decimal system, we show an equivalent table with powers of $10$ as the digits:\n", "\n", "|Decimal| 3 | 2 | 1 | 0 |\n", "|-------|------|------|------|------|\n", "| Digit |$10^3$|$10^2$|$10^1$|$10^0$|\n", "| $=$ |$1000$| $100$| $10$ | $1$ |\n", "\n", "Now, an integer is a linear combination of the digits where the coefficients are one of *ten* values, and the base is now $10$. For example, the number $123$ can be expressed as $0*1000 + 1*100 + 2*10 + 3*1$. So, the binary representation follows the same logic as the decimal system taught in grade school. The decimal system is intuitive to us humans, mostly as we learn to count with our *ten* fingers. The $0$s and $1$s in a computer's memory are therefore no rocket science; they only feel unintuitive for a beginner." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Hexadecimal Representations" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "While in the binary and decimal systems there are two and ten distinct coefficients per digit, another convenient representation, the **hexadecimal representation**, uses a base of $16$. It is convenient as one digit stores the same amount of information as *four* bits and the binary representation quickly becomes unreadable for larger numbers. The letters \"a\" through \"f\" are used as digits \"10\" through \"15\".\n", "\n", "The following table summarizes the relationship between the three systems:" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "|Decimal|Hexadecimal|Binary|$~~~~~~$|Decimal|Hexadecimal|Binary|$~~~~~~$|Decimal|Hexadecimal|Binary|$~~~~~~$|...|\n", "|-------|-----------|------|--------|-------|-----------|------|--------|-------|-----------|------|--------|---|\n", "| 0 | 0 | 0000 |$~~~~~~$| 16 | 10 | 10000|$~~~~~~$| 32 | 20 |100000|$~~~~~~$|...|\n", "| 1 | 1 | 0001 |$~~~~~~$| 17 | 11 | 10001|$~~~~~~$| 33 | 21 |100001|$~~~~~~$|...|\n", "| 2 | 2 | 0010 |$~~~~~~$| 18 | 12 | 10010|$~~~~~~$| 34 | 22 |100010|$~~~~~~$|...|\n", "| 3 | 3 | 0011 |$~~~~~~$| 19 | 13 | 10011|$~~~~~~$| 35 | 23 |100011|$~~~~~~$|...|\n", "| 4 | 4 | 0100 |$~~~~~~$| 20 | 14 | 10100|$~~~~~~$| 36 | 24 |100100|$~~~~~~$|...|\n", "| 5 | 5 | 0101 |$~~~~~~$| 21 | 15 | 10101|$~~~~~~$| 37 | 25 |100101|$~~~~~~$|...|\n", "| 6 | 6 | 0110 |$~~~~~~$| 22 | 16 | 10110|$~~~~~~$| 38 | 26 |100110|$~~~~~~$|...|\n", "| 7 | 7 | 0111 |$~~~~~~$| 23 | 17 | 10111|$~~~~~~$| 39 | 27 |100111|$~~~~~~$|...|\n", "| 8 | 8 | 1000 |$~~~~~~$| 24 | 18 | 11000|$~~~~~~$| 40 | 28 |101000|$~~~~~~$|...|\n", "| 9 | 9 | 1001 |$~~~~~~$| 25 | 19 | 11001|$~~~~~~$| 41 | 29 |101001|$~~~~~~$|...|\n", "| 10 | a | 1010 |$~~~~~~$| 26 | 1a | 11010|$~~~~~~$| 42 | 2a |101010|$~~~~~~$|...|\n", "| 11 | b | 1011 |$~~~~~~$| 27 | 1b | 11011|$~~~~~~$| 43 | 2b |101011|$~~~~~~$|...|\n", "| 12 | c | 1100 |$~~~~~~$| 28 | 1c | 11100|$~~~~~~$| 44 | 2c |101100|$~~~~~~$|...|\n", "| 13 | d | 1101 |$~~~~~~$| 29 | 1d | 11101|$~~~~~~$| 45 | 2d |101101|$~~~~~~$|...|\n", "| 14 | e | 1110 |$~~~~~~$| 30 | 1e | 11110|$~~~~~~$| 46 | 2e |101110|$~~~~~~$|...|\n", "| 15 | f | 1111 |$~~~~~~$| 31 | 1f | 11111|$~~~~~~$| 47 | 2f |101111|$~~~~~~$|...|" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "To show more examples of the above subscript convention, we pick three random entries from the table:\n", "\n", "$11_{10} = \\text{b}_{16} = 1011_2$\n", "\n", "$25_{10} = 19_{16} = 11001_2$\n", "\n", "$46_{10} = 2\\text{e}_{16} = 101110_2$\n", "\n", "The built-in [hex()](https://docs.python.org/3/library/functions.html#hex) function creates a `str` object starting with `\"0x\"` representing an `int` object's hexadecimal representation. The length depends on how many groups of four bits are implied by the corresponding binary representation.\n", "\n", "For `0` and `1`, the hexadecimal representation is similar to the binary one." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0x0'" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(0)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0x1'" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(1)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Whereas `bin(3)` already requires two digits, one is enough for `hex(3)`." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'0x3'" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(3) # bin(3) => \"0b11\"; two digits needed" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "For `10` and `15`, we see the letter digits for the first time." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'0xa'" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(10)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0xf'" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(15)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The binary representation of `177`, `\"0b10110001\"`, can be viewed as *two* groups of four bits, $1011$ and $0001$, that are encoded as $\\text{b}$ and $1$ in hexadecimal (cf., table above)." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0b10110001'" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(177)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0xb1'" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(177)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "To obtain a *new* `int` object with the value `177`, we call the [int()](https://docs.python.org/3/library/functions.html#int) built-in with a properly formatted `str` object and `base=16` as arguments." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "177" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "int(\"0xb1\", base=16)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Alternatively, we could use a literal notation instead." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "177" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "0xb1" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Hexadecimals between $00_{16}$ and $\\text{ff}_{16}$ (i.e., $0_{10}$ and $255_{10}$) are commonly used to describe colors, for example, in web development but also graphics editors. See this [online tool](https://www.w3schools.com/colors/colors_hexadecimal.asp) for some more background." ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "'0xff'" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(255)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Just like binary representations, the hexadecimals extend to the left for larger numbers like `789`." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "'0x315'" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(i) # = hex(789)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "For completeness sake, we mention that there is also the [oct()](https://docs.python.org/3/library/functions.html#oct) built-in to obtain an integer's **octal representation**. The logic is the same as for the hexadecimal representation, and we use *eight* instead of *sixteen* digits. That is the equivalent of viewing the binary representations in groups of three bits. As of today, octal representations have become less important, and the data science practitioner may probably live without them quite well." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "### Negative Values" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "While there are conventions that model negative integers with $0$s and $1$s in memory (cf., [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement)), Python manages that for us, and we do not look into the theory here for brevity. We have learned all we need to know about how integers are modeled in a computer.\n", "\n", "The binary and hexadecimal representations of negative integers are identical to their positive counterparts except that they start with a minus sign `-`." ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "'-0b11'" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(-3)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "'-0x3'" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(-3)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "'-0b11111111'" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(-255)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "'-0xff'" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(-255)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### The `bool` Type" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Whereas the boolean literals `True` and `False` are commonly *not* regarded as numeric types, they behave like `1` and `0` in an arithmetic context." ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "True + False" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "42" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "41 + True" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "0.0" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "42.0 * False" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "We may explicitly cast `bool` objects as integers ourselves with the [int()](https://docs.python.org/3/library/functions.html#int) built-in." ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "int(True)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "int(False)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Of course, their binary representations only need *one* bit of information." ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0b1'" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(True)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0b0'" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(False)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Their hexadecimal representations occupy *four* bits in memory while only *one* bit is needed. This is because \"1\" and \"0\" are just two of the sixteen possible digits." ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0x1'" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(True)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0x0'" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(False)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "As a reminder, the `None` literal is a type on its own, namely the `NoneType`, and different from `False`. It *cannot* be cast as an integer as the `TypeError` indicates." ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "ename": "TypeError", "evalue": "int() argument must be a string, a bytes-like object or a number, not 'NoneType'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: int() argument must be a string, a bytes-like object or a number, not 'NoneType'" ] } ], "source": [ "int(None)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Bitwise Operators" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Now that we know how integers are represented with $0$s and $1$s, we look at ways of working with the individual bits, in particular with the so-called **[bitwise operators](https://wiki.python.org/moin/BitwiseOperators)**: As the name suggests, the operators perform some operation on a bit by bit basis. They only work with and always return `int` objects.\n", "\n", "We keep this overview rather short as such \"low-level\" operations are not needed by the data science practitioner regularly. Yet, it is worthwhile to have heard about them as they form the basis of all of arithmetic in computers.\n", "\n", "The first operator is the **bitwise AND** operator `&`: It looks at the bits of its two operands, `11` and `13` in the example, in a pairwise fashion and *iff* both operands have a $1$ in the same position, will the resulting integer have a $1$ in this position as well. The binary representations of `11` and `13` have $1$s in their respective first and fourth bits, which is why `bin(11 & 13)` evaluates to `\"Ob1001\"`." ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0b1011 & 0b1101'" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(11) + \" & \" + bin(13) # to show the operands' binary representations" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0b1001'" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(11 & 13)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`0b1001` is the binary representation of `9`." ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "0b1001" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "11 & 13" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The **bitwise OR** operator `|` evaluates to an `int` object whose bits are set to $1$ if the corresponding bits of either *one* or *both* operands are $1$. So in the example `9 | 13` only the second bit is $0$ for both operands, which is why the expression evaluates to `\"0b1101\"`." ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0b1001 | 0b1101'" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(9) + \" | \" + bin(13) # to show the operands' binary representations" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0b1101'" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(9 | 13)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`0b1101` evaluates to the `int` object `13`." ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "13" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "0b1101" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "13" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "9 | 13" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The **bitwise XOR** operator `^` is a special case of the `|` operator in that it evaluates to an `int` object whose bits are set to $1$ if the corresponding bit of *exactly one* of the two operands is $1$. Colloquially, the \"X\" stands for \"exclusive.\" The `^` operator must *not* be confused with the exponentiation operator `**`! In the example, `9 ^ 13`, only the third bit differs between the two operands, which is why it evaluates to `\"0b100\"` omitting the fourth bit." ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0b1001 ^ 0b1101'" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(9) + \" ^ \" + bin(13) # to show the operands' binary representations" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0b100'" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(9 ^ 13)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`0b100` evaluates to the `int` object `4`." ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "0b100" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "9 ^ 13" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The **bitwise NOT** operator `~`, sometimes also called **inversion** operator, is said to \"flip\" the $0$s into $1$s and the $1$s into $0$s. However, it is based on the aforementioned [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement) convention and `~x = -(x + 1)` by definition (cf., the [reference](https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations)). The full logic behind this is considered out of scope in this book.\n", "\n", "We can at least verify the definition by comparing the binary representations of `8` and `-9`: They are indeed the same." ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'-0b1001'" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(~8)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'-0b1001'" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(-(8 + 1))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`~x = -(x + 1)` can be reformulated as `~x + x = -1`, which is slightly easier to check." ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "-1" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "~8 + 8" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Lastly, the **bitwise left and right shift** operators, `<<` and `>>`, shift all the bits either to the left or to the right. This corresponds to multiplying or dividing an integer by powers of $2$.\n", "\n", "When shifting left, $0$s are filled in." ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0b1001'" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(9)" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0b100100'" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(9 << 2)" ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "36" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "9 << 2" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "When shifting right, some bits are always lost." ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'0b10'" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bin(9 >> 2)" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "9 >> 2" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## The `float` Type" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "As we have seen above, some assumptions need to be made as to how the $0$s and $1$s in a computer's memory are to be translated into numbers. This process becomes a lot more involved when we go beyond integers and model [real numbers](https://en.wikipedia.org/wiki/Real_number) (i.e., the set $\\mathbb{R}$) with possibly infinitely many digits to the right of the period like $1.23$.\n", "\n", "The **[Institute of Electrical and Electronics Engineers](https://en.wikipedia.org/wiki/Institute_of_Electrical_and_Electronics_Engineers)** (IEEE, pronounced \"eye-triple-E\") is one of the important professional associations when it comes to standardizing all kinds of aspects regarding the implementation of soft- and hardware.\n", "\n", "The **[IEEE 754](https://en.wikipedia.org/wiki/IEEE_754)** standard defines the so-called **floating-point arithmetic** that is commonly used today by all major programming languages. The standard not only defines how the $0$s and $1$s are organized in memory but also, for example, how values are to be rounded, what happens in exceptional cases like divisions by zero, or what is a zero value in the first place.\n", "\n", "In Python, the simplest way to create a `float` object is to use a literal notation with a dot `.` in it." ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "f = 1.23" ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "140166567715664" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "id(f)" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "float" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(f)" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "1.23" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "As with integer literals above, we may use underscores `_` to make longer `float` objects easier to read." ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "0.123456789" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "0.123_456_789" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "In cases where the dot `.` is unnecessary from a mathematical point of view, we either need to end the number with it nevertheless or use the [float()](https://docs.python.org/3/library/functions.html#float) built-in to cast the number explicitly. [float()](https://docs.python.org/3/library/functions.html#float) can process any numeric object or a properly formatted `str` object." ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1. # 1 without a dot creates an int object" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(1)" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"1.000\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`float` objects are implicitly created as the result of dividing an `int` object by another with the division operator `/`." ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "0.3333333333333333" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1 / 3" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "In general, if we combine `float` and `int` objects in arithmetic operations, we always end up with a `float` type: Python uses the \"broader\" representation." ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "42.0" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "40.0 + 2" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "42.0" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "21 * 2.0" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Scientific Notation" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`float` objects may also be created with the **scientific literal notation**: We use the symbol `e` to indicate powers of $10$, so $1.23 * 10^0$ translates into `1.23e0`." ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "1.23" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1.23e0" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Syntactically, `e` needs a `float` or `int` object in its literal notation on its left and an `int` object on its right, both without a space. Otherwise, we get a `SyntaxError`." ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "ename": "SyntaxError", "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23 e0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], "source": [ "1.23 e0" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "ename": "SyntaxError", "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e 0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], "source": [ "1.23e 0" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "ename": "SyntaxError", "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e0.0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], "source": [ "1.23e0.0" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "If we leave out the number to the left, Python raises a `NameError` as it unsuccessfully tries to look up a variable named `e1`." ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "ename": "NameError", "evalue": "name 'e1' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0me1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'e1' is not defined" ] } ], "source": [ "e1" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "So, to write $10^1$ in Python, we need to think of it as $1*10^1$ and write `1e1`." ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "10.0" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1e1" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Special Values" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "There are also three special values representing \"**not a number,**\" called `nan`, and positive or negative **infinity**, called `inf` or `-inf`, that are created by passing in the corresponding abbreviation as a `str` object to the [float()](https://docs.python.org/3/library/functions.html#float) built-in. These values could be used, for example, as the result of a mathematically undefined operation like division by zero or to model the value of a mathematical function as it goes to infinity." ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "nan" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"nan\") # also works as float(\"NaN\")" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "inf" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"+inf\") # also works as float(\"+infinity\")" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "inf" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"inf\") # by omitting the plus sign we mean positive infinity" ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "-inf" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"-inf\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`nan` objects *never* compare equal to *anything*, not even to themselves. This happens in accordance with the [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard." ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"nan\") == float(\"nan\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "On the contrary, as two values go to infinity, there is no such concept as difference and *everything* compares equal." ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"inf\") == float(\"inf\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Adding `42` to `inf` makes no difference." ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "inf" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"inf\") + 42" ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"inf\") + 42 == float(\"inf\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "We observe the same for multiplication ..." ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "inf" ] }, "execution_count": 92, "metadata": {}, "output_type": "execute_result" } ], "source": [ "42 * float(\"inf\")" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "42 * float(\"inf\") == float(\"inf\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "... and even exponentiation!" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "inf" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"inf\") ** 42" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"inf\") ** 42 == float(\"inf\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Although absolute differences become unmeaningful as we approach infinity, signs are still respected." ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "inf" ] }, "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ "-42 * float(\"-inf\")" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "-42 * float(\"-inf\") == float(\"inf\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "As a caveat, adding infinities of different signs is an *undefined operation* in math and results in a `nan` object. So, if we (accidentally or unknowingly) do this on a real dataset, we do *not* see any error messages, and our program may continue to run with non-meaningful results! This is an example of a piece of code **failing silently**." ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "nan" ] }, "execution_count": 98, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"inf\") + float(\"-inf\")" ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "nan" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "float(\"inf\") - float(\"inf\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Imprecision" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`float` objects are *inherently* imprecise, and there is *nothing* we can do about it! In particular, arithmetic operations with two `float` objects may result in \"weird\" rounding \"errors\" that are strictly deterministic and occur in accordance with the [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard.\n", "\n", "For example, let's add `1e0` to `1e15` and `1e16`, respectively. In the latter case, the `1e0` somehow gets \"lost.\"" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "1000000000000001.0" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1e15 + 1e0" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "1e+16" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1e16 + 1e0" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Of course, we may also add the `int` object `1` to the `float` objects in the literal `e` notation with the same outcome." ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "1000000000000001.0" ] }, "execution_count": 102, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1e15 + 1" ] }, { "cell_type": "code", "execution_count": 103, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "1e+16" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1e16 + 1" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Interactions between sufficiently large and small `float` objects are not the only source of imprecision." ] }, { "cell_type": "code", "execution_count": 104, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "from math import sqrt" ] }, { "cell_type": "code", "execution_count": 105, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "2.0000000000000004" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sqrt(2) ** 2" ] }, { "cell_type": "code", "execution_count": 106, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0.30000000000000004" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "0.1 + 0.2" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "This may become a problem if we rely on equality checks in our programs." ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sqrt(2) ** 2 == 2" ] }, { "cell_type": "code", "execution_count": 108, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "0.1 + 0.2 == 0.3" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "A popular workaround is to benchmark the difference between the two numbers to be checked for equality against a pre-defined `threshold` *sufficiently* close to `0`, for example, `1e-15`." ] }, { "cell_type": "code", "execution_count": 109, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "threshold = 1e-15" ] }, { "cell_type": "code", "execution_count": 110, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(sqrt(2) ** 2) - 2 < threshold" ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(0.1 + 0.2) - 0.3 < threshold" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The built-in [format()](https://docs.python.org/3/library/functions.html#format) function allows us to show the **significant digits** of a `float` number as they exist in memory to arbitrary precision. To exemplify it, let's view a couple of `float` objects with `50` digits. This analysis reveals that almost no `float` number is precise! After $14$ or $15$ digits \"weird\" things happen. As we see further below, the \"random\" digits ending the `float` numbers do *not* \"physically\" exist in memory.\n", "\n", "The [format()](https://docs.python.org/3/library/functions.html#format) function is different from the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method on `str` objects introduced in the next chapter and both work with the so-called [format specification mini-language](https://docs.python.org/3/library/string.html#format-specification-mini-language): `\".50f\"` is the instruction to show `50` digits of a `float` number. But let's not worry too much about these details for now." ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0.10000000000000000555111512312578270211815834045410'" ] }, "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(0.1, \".50f\")" ] }, { "cell_type": "code", "execution_count": 113, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0.20000000000000001110223024625156540423631668090820'" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(0.2, \".50f\")" ] }, { "cell_type": "code", "execution_count": 114, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0.29999999999999998889776975374843459576368331909180'" ] }, "execution_count": 114, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(0.3, \".50f\")" ] }, { "cell_type": "code", "execution_count": 115, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0.33333333333333331482961625624739099293947219848633'" ] }, "execution_count": 115, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(1 / 3, \".50f\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The [format()](https://docs.python.org/3/library/functions.html#format) function does *not* round a `float` object in the mathematical sense! It just allows us to show an arbitrary number of the digits as stored in memory, and it also does *not* change these.\n", "\n", "On the contrary, the built-in [round()](https://docs.python.org/3/library/functions.html#round) function creates a *new* numeric object that is a rounded version of the one passed in as the argument. It adheres to the common rules of math.\n", "\n", "For example, let's round `1 / 3` to five decimals. The obtained value for `roughly_a_third` is also *imprecise* but different from the \"exact\" representation of `1 / 3` above." ] }, { "cell_type": "code", "execution_count": 116, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "roughly_a_third = round(1 / 3, 5)" ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "0.33333" ] }, "execution_count": 117, "metadata": {}, "output_type": "execute_result" } ], "source": [ "roughly_a_third" ] }, { "cell_type": "code", "execution_count": 118, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0.33333000000000001517008740847813896834850311279297'" ] }, "execution_count": 118, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(roughly_a_third, \".50f\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Surprisingly, `0.125` and `0.25` appear to be *precise*, and equality comparison works without the `threshold` workaround: Both are powers of $2$ in disguise." ] }, { "cell_type": "code", "execution_count": 119, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "'0.12500000000000000000000000000000000000000000000000'" ] }, "execution_count": 119, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(0.125, \".50f\")" ] }, { "cell_type": "code", "execution_count": 120, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0.25000000000000000000000000000000000000000000000000'" ] }, "execution_count": 120, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(0.25, \".50f\")" ] }, { "cell_type": "code", "execution_count": 121, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 121, "metadata": {}, "output_type": "execute_result" } ], "source": [ "0.125 + 0.125 == 0.25" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Binary Representations" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "To understand these subtleties, we need to look at the **[binary representation of floats](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)** and review the basics of the **[IEEE 754](https://en.wikipedia.org/wiki/IEEE_754)** standard. On modern machines, floats are modeled in so-called double precision with $64$ bits that are grouped as in the figure below. The first bit determines the sign ($0$ for plus, $1$ for minus), the next $11$ bits represent an $exponent$ term, and the last $52$ bits resemble the actual significant digits, the so-called $fraction$ part. The three groups are put together like so:\n", "\n", "$$float = (-1)^{sign} * 1.fraction * 2^{exponent-1023}$$\n", "\n", "A $1.$ is implicitly prepended as the first digit, and both, $fraction$ and $exponent$, are stored in base $2$ representation (i.e., they both are interpreted like integers above). As $exponent$ is consequently non-negative, between $0_{10}$ and $2047_{10}$ to be precise, the $-1023$, called the exponent bias, centers the entire $2^{exponent-1023}$ term around $1$ and allows the period within the $1.fraction$ part be shifted into either direction by the same amount. Floating-point numbers received their name as the period, formally called the **[radix point](https://en.wikipedia.org/wiki/Radix_point)**, \"floats\" along the significant digits. As an aside, an $exponent$ of all $0$s or all $1$s is used to model the special values `nan` or `inf`.\n", "\n", "As the standard defines the exponent part to come as a power of $2$, we now see why `0.125` is a *precise* float: It can be represented as a power of $2$, i.e., $0.125 = (-1)^0 * 1.0 * 2^{1020-1023} = 2^{-3} = \\frac{1}{8}$. In other words, the floating-point representation of $0.125_{10}$ is $0_2$, $1111111100_2 = 1020_{10}$, and $0_2$ for the three groups, respectively." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The crucial fact for the data science practitioner to understand is that mapping the *infinite* set of the real numbers $\\mathbb{R}$ to a *finite* set of bits leads to the imprecisions shown above!\n", "\n", "So, floats are usually good approximations of real numbers only with their first $14$ or $15$ digits. If more precision is required, we need to revert to other data types such as a `Decimal` or a `Fraction`, as shown in the next two sections.\n", "\n", "This [blog post](http://fabiensanglard.net/floating_point_visually_explained/) gives another neat and *visual* way as to how to think of floats. It also explains why floats become worse approximations of the reals as their absolute values increase.\n", "\n", "The Python [documentation](https://docs.python.org/3/tutorial/floatingpoint.html) provides another good discussion of floats and the goodness of their approximations.\n", "\n", "If we are interested in the exact bits behind a `float` object, we use the [hex()](https://docs.python.org/3/library/stdtypes.html#float.hex) method that returns a `str` object beginning with `\"0x1.\"` followed by the $fraction$ in hexadecimal notation and the $exponent$ as an integer after subtraction of $1023$ and separated by a `\"p\"`." ] }, { "cell_type": "code", "execution_count": 122, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "one_eighth = 1 / 8" ] }, { "cell_type": "code", "execution_count": 123, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0x1.0000000000000p-3'" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "one_eighth.hex() # this basically says 2 ** (-3)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Also, the [as_integer_ratio()](https://docs.python.org/3/library/stdtypes.html#float.as_integer_ratio) method returns the two smallest integers whose ratio best approximates a `float` object." ] }, { "cell_type": "code", "execution_count": 124, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "(1, 8)" ] }, "execution_count": 124, "metadata": {}, "output_type": "execute_result" } ], "source": [ "one_eighth.as_integer_ratio()" ] }, { "cell_type": "code", "execution_count": 125, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'0x1.555475a31a4bep-2'" ] }, "execution_count": 125, "metadata": {}, "output_type": "execute_result" } ], "source": [ "roughly_a_third.hex()" ] }, { "cell_type": "code", "execution_count": 126, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "(3002369727582815, 9007199254740992)" ] }, "execution_count": 126, "metadata": {}, "output_type": "execute_result" } ], "source": [ "roughly_a_third.as_integer_ratio()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`0.0` is also a power of $2$ and thus a *precise* `float` number." ] }, { "cell_type": "code", "execution_count": 127, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "zero = 0.0" ] }, { "cell_type": "code", "execution_count": 128, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0x0.0p+0'" ] }, "execution_count": 128, "metadata": {}, "output_type": "execute_result" } ], "source": [ "zero.hex()" ] }, { "cell_type": "code", "execution_count": 129, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "(0, 1)" ] }, "execution_count": 129, "metadata": {}, "output_type": "execute_result" } ], "source": [ "zero.as_integer_ratio()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "As seen in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#%28Data%29-Type-%2F-%22Behavior%22), the [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) method tells us if a `float` can be casted as an `int` object without any loss in precision." ] }, { "cell_type": "code", "execution_count": 130, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 130, "metadata": {}, "output_type": "execute_result" } ], "source": [ "roughly_a_third.is_integer()" ] }, { "cell_type": "code", "execution_count": 131, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 131, "metadata": {}, "output_type": "execute_result" } ], "source": [ "one = roughly_a_third / roughly_a_third\n", "\n", "one.is_integer()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "As the exact implementation of floats may vary and be dependent on a particular Python installation, we look up the [float_info](https://docs.python.org/3/library/sys.html#sys.float_info) attribute in the [sys](https://docs.python.org/3/library/sys.html) module in the [standard library](https://docs.python.org/3/library/index.html) to check the details. Usually, this is not necessary." ] }, { "cell_type": "code", "execution_count": 132, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import sys" ] }, { "cell_type": "code", "execution_count": 133, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)" ] }, "execution_count": 133, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sys.float_info" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## The `Decimal` Type" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides a [Decimal](https://docs.python.org/3/library/decimal.html#decimal.Decimal) type that may be used to represent any real number to a user-defined level of precision: \"User-defined\" does *not* mean an infinite or \"exact\" precision! The `Decimal` type merely allows us to work with a number of bits *different* from the $64$ as specified for the `float` type and also to customize the rounding rules and some other settings.\n", "\n", "We import the `Decimal` type and also the [getcontext()](https://docs.python.org/3/library/decimal.html#decimal.getcontext) function from the [decimal](https://docs.python.org/3/library/decimal.html) module." ] }, { "cell_type": "code", "execution_count": 134, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "from decimal import Decimal, getcontext" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "[getcontext()](https://docs.python.org/3/library/decimal.html#decimal.getcontext) shows us how the [decimal](https://docs.python.org/3/library/decimal.html) module is set up. By default, the precision is set to `28` significant digits, which is roughly twice as many as with `float` objects." ] }, { "cell_type": "code", "execution_count": 135, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])" ] }, "execution_count": 135, "metadata": {}, "output_type": "execute_result" } ], "source": [ "getcontext()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The two simplest ways to create a `Decimal` object is to either **instantiate** it with an `int` or a `str` object consisting of all the significant digits. In the latter case, the scientific notation is also possible." ] }, { "cell_type": "code", "execution_count": 136, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('42')" ] }, "execution_count": 136, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(42)" ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('0.1')" ] }, "execution_count": 137, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(\"0.1\")" ] }, { "cell_type": "code", "execution_count": 138, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('1E+5')" ] }, "execution_count": 138, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(\"1e5\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "It is *not* a good idea to create a `Decimal` from a `float` object. If we did so, we would create a `Decimal` object that internally used extra bits to store the \"random\" digits that are not stored in the `float` object in the first place." ] }, { "cell_type": "code", "execution_count": 139, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('0.1000000000000000055511151231257827021181583404541015625')" ] }, "execution_count": 139, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(0.1) # do not create a Decimal from a float" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "With the `Decimal` type, the imprecisions in the arithmetic and equality comparisons from above go away." ] }, { "cell_type": "code", "execution_count": 140, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('0.3')" ] }, "execution_count": 140, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(\"0.1\") + Decimal(\"0.2\")" ] }, { "cell_type": "code", "execution_count": 141, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 141, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(\"0.1\") + Decimal(\"0.2\") == Decimal(\"0.3\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`Decimal` numbers *preserve* the **significant digits**, even in cases where this is not needed." ] }, { "cell_type": "code", "execution_count": 142, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('0.30000')" ] }, "execution_count": 142, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(\"0.10000\") + Decimal(\"0.20000\")" ] }, { "cell_type": "code", "execution_count": 143, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 143, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(\"0.10000\") + Decimal(\"0.20000\") == Decimal(\"0.3\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Arithmetic operations between `Decimal` and `int` objects work as the latter are inherently precise: The results are *new* `Decimal` objects." ] }, { "cell_type": "code", "execution_count": 144, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('42')" ] }, "execution_count": 144, "metadata": {}, "output_type": "execute_result" } ], "source": [ "42 + Decimal(42) - 42" ] }, { "cell_type": "code", "execution_count": 145, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('42')" ] }, "execution_count": 145, "metadata": {}, "output_type": "execute_result" } ], "source": [ "10 * Decimal(42) / 10" ] }, { "cell_type": "code", "execution_count": 146, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('0.1')" ] }, "execution_count": 146, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(1) / 10" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "To verify the precision, we apply the built-in [format()](https://docs.python.org/3/library/functions.html#format) function to the previous code cell and compare it with the same division resulting in a `float` object." ] }, { "cell_type": "code", "execution_count": 147, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'0.10000000000000000000000000000000000000000000000000'" ] }, "execution_count": 147, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(Decimal(1) / 10, \".50f\")" ] }, { "cell_type": "code", "execution_count": 148, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "'0.10000000000000000555111512312578270211815834045410'" ] }, "execution_count": 148, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(1 / 10, \".50f\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "However, mixing `Decimal` and `float` objects raises a `TypeError`: So, Python prevents us from potentially introducing imprecisions via innocent-looking arithmetic by **failing loudly**." ] }, { "cell_type": "code", "execution_count": 149, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "ename": "TypeError", "evalue": "unsupported operand type(s) for *: 'float' and 'decimal.Decimal'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1.0\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for *: 'float' and 'decimal.Decimal'" ] } ], "source": [ "1.0 * Decimal(42)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "To preserve the precision for more advanced mathematical functions, `Decimal` objects come with many **methods bound** on them. For example, [ln()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. In general, the functions in the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html) should only be used with `float` objects as they do *not* preserve precision." ] }, { "cell_type": "code", "execution_count": 150, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('2')" ] }, "execution_count": 150, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(100).log10()" ] }, { "cell_type": "code", "execution_count": 151, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('1.414213562373095048801688724')" ] }, "execution_count": 151, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(2).sqrt()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The object returned by the [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) method is still limited in precision: This must be so as, for example, $\\sqrt{2}$ is an **[irrational number](https://en.wikipedia.org/wiki/Irrational_number)** that *cannot* be expressed with absolute precision using *any* number of bits, even in theory.\n", "\n", "We see this as raising $\\sqrt{2}$ to the power of $2$ results in an imprecise value as before!" ] }, { "cell_type": "code", "execution_count": 152, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('1.999999999999999999999999999')" ] }, "execution_count": 152, "metadata": {}, "output_type": "execute_result" } ], "source": [ "two = Decimal(2).sqrt() ** 2\n", "\n", "two" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method allows us to [quantize](https://www.dictionary.com/browse/quantize) (i.e., \"round\") a `Decimal` number at any precision that is *smaller* than the set precision. It looks at the number of decimals (i.e., to the right of the period) of the numeric argument we pass in.\n", "\n", "For example, as the overall imprecise value of `two` still has an internal precision of `28` digits, we can correctly round it to *four* decimals (i.e., `Decimal(\"0.0001\")` has four decimals)." ] }, { "cell_type": "code", "execution_count": 153, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('2.0000')" ] }, "execution_count": 153, "metadata": {}, "output_type": "execute_result" } ], "source": [ "two.quantize(Decimal(\"0.0001\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can never round a `Decimal` number and obtain a greater precision than before: The `InvalidOperation` exception tells us that *loudly*." ] }, { "cell_type": "code", "execution_count": 154, "metadata": {}, "outputs": [ { "ename": "InvalidOperation", "evalue": "[]", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtwo\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquantize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"0.1\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m**\u001b[0m \u001b[0;36m28\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } ], "source": [ "two.quantize(Decimal(\"0.1\") ** 28)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Consequently, with this little workaround $\\sqrt{2}^2 = 2$ works, even in Python." ] }, { "cell_type": "code", "execution_count": 155, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 155, "metadata": {}, "output_type": "execute_result" } ], "source": [ "two.quantize(Decimal(\"0.0001\")) == 2" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The downside is that the entire expression is not as pretty as `sqrt(2) ** 2 == 2` from above." ] }, { "cell_type": "code", "execution_count": 156, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 156, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(Decimal(2).sqrt() ** 2).quantize(Decimal(\"0.0001\")) == 2" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`nan` and positive and negative `inf` exist as well, and the same remarks from above apply." ] }, { "cell_type": "code", "execution_count": 157, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('NaN')" ] }, "execution_count": 157, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(\"nan\")" ] }, { "cell_type": "code", "execution_count": 158, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 158, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(\"nan\") == Decimal(\"nan\") # nan's never compare equal to anything, not even to themselves" ] }, { "cell_type": "code", "execution_count": 159, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('Infinity')" ] }, "execution_count": 159, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(\"inf\")" ] }, { "cell_type": "code", "execution_count": 160, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('-Infinity')" ] }, "execution_count": 160, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(\"-inf\")" ] }, { "cell_type": "code", "execution_count": 161, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('Infinity')" ] }, "execution_count": 161, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(\"inf\") + 42 # Infinity is infinity, concrete numbers loose its meaning" ] }, { "cell_type": "code", "execution_count": 162, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 162, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Decimal(\"inf\") + 42 == Decimal(\"inf\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "As with `float` objects, we cannot add infinities of different signs: Now get a module-specific `InvalidOperation` exception instead of a `nan` value. Here, **failing loudly** is a good thing as it prevents us from working with invalid results." ] }, { "cell_type": "code", "execution_count": 163, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "ename": "InvalidOperation", "evalue": "[]", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"-inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } ], "source": [ "Decimal(\"inf\") + Decimal(\"-inf\")" ] }, { "cell_type": "code", "execution_count": 164, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "ename": "InvalidOperation", "evalue": "[]", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } ], "source": [ "Decimal(\"inf\") - Decimal(\"inf\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "For more information on the `Decimal` type, see the tutorial at [PYMOTW](https://pymotw.com/3/decimal/index.html) or the official [documentation](https://docs.python.org/3/library/decimal.html)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## The `Fraction` Type" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "If the numbers in an application can be expressed as [rational numbers](https://en.wikipedia.org/wiki/Rational_number) (i.e., the set $\\mathbb{Q}$), we may model them as a [Fraction](https://docs.python.org/3/library/fractions.html#fractions.Fraction) type from the [fractions](https://docs.python.org/3/library/fractions.html) module in the [standard library](https://docs.python.org/3/library/index.html). As any fraction can always be formulated as the division of one integer by another, `Fraction` objects are inherently precise, just as `int` objects on their own. Further, we maintain the precision as long as we do not use them in a mathematical operation that could result in an irrational number (e.g., taking the square root).\n", "\n", "We import the `Fraction` type from the [fractions](https://docs.python.org/3/library/fractions.html) module." ] }, { "cell_type": "code", "execution_count": 165, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "from fractions import Fraction" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Among others, there are two simple ways to create a `Fraction` object: We either instantiate one with two `int` objects representing the numerator and denominator or with a `str` object. In the latter case, we have two options again and use either the format \"numerator/denominator\" (i.e., *without* any spaces) or the same format as for `float` and `Decimal` objects above." ] }, { "cell_type": "code", "execution_count": 166, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "Fraction(1, 3)" ] }, "execution_count": 166, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Fraction(1, 3) # this is now 1/3 with \"full\" precision" ] }, { "cell_type": "code", "execution_count": 167, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Fraction(1, 3)" ] }, "execution_count": 167, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Fraction(\"1/3\") # this is 1/3 with \"full\" precision again" ] }, { "cell_type": "code", "execution_count": 168, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Fraction(3333333333, 10000000000)" ] }, "execution_count": 168, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Fraction(\"0.3333333333\") # this is 1/3 with a precision of 10 significant digits" ] }, { "cell_type": "code", "execution_count": 169, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Fraction(3333333333, 10000000000)" ] }, "execution_count": 169, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Fraction(\"3333333333e-10\") # the same in scientific notation" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Only the lowest common denominator version is maintained after creation: For example, $\\frac{3}{2}$ and $\\frac{6}{4}$ are the same, and both become `Fraction(3, 2)`." ] }, { "cell_type": "code", "execution_count": 170, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "Fraction(3, 2)" ] }, "execution_count": 170, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Fraction(3, 2)" ] }, { "cell_type": "code", "execution_count": 171, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Fraction(3, 2)" ] }, "execution_count": 171, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Fraction(6, 4)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "We could also cast a `Decimal` object as a `Fraction` object: This only makes sense as `Decimal` objects come with a pre-defined precision." ] }, { "cell_type": "code", "execution_count": 172, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "Fraction(1, 10)" ] }, "execution_count": 172, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Fraction(Decimal(\"0.1\"))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`float` objects may *syntactically* be cast as `Fraction` objects as well. However, then we create a `Fraction` object that precisely remembers the `float` object's imprecision: A *bad* idea!" ] }, { "cell_type": "code", "execution_count": 173, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Fraction(3602879701896397, 36028797018963968)" ] }, "execution_count": 173, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Fraction(0.1)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`Fraction` objects follow the arithmetic rules from middle school and may be mixed with `int` objects *without* any loss of precision. The result is always a *new* `Fraction` object." ] }, { "cell_type": "code", "execution_count": 174, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "Fraction(7, 4)" ] }, "execution_count": 174, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Fraction(3, 2) + Fraction(1, 4)" ] }, { "cell_type": "code", "execution_count": 175, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Fraction(1, 2)" ] }, "execution_count": 175, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Fraction(5, 2) - 2" ] }, { "cell_type": "code", "execution_count": 176, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Fraction(1, 1)" ] }, "execution_count": 176, "metadata": {}, "output_type": "execute_result" } ], "source": [ "3 * Fraction(1, 3)" ] }, { "cell_type": "code", "execution_count": 177, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Fraction(1, 1)" ] }, "execution_count": 177, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Fraction(3, 2) * Fraction(2, 3)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`Fraction` and `float` objects may also be mixed *syntactically*. However, then the results may exhibit imprecision again, even if we do not see them at first sight! This is another example of code **failing silently**." ] }, { "cell_type": "code", "execution_count": 178, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "0.1" ] }, "execution_count": 178, "metadata": {}, "output_type": "execute_result" } ], "source": [ "10.0 * Fraction(1, 100)" ] }, { "cell_type": "code", "execution_count": 179, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'0.10000000000000000555111512312578270211815834045410'" ] }, "execution_count": 179, "metadata": {}, "output_type": "execute_result" } ], "source": [ "format(10.0 * Fraction(1, 100), \".50f\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "For more examples and discussions, see the tutorial at [PYMOTW](https://pymotw.com/3/fractions/index.html) or the official [documentation](https://docs.python.org/3/library/fractions.html)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## The `complex` Type" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Some mathematical equations cannot be solved if the solution has to be in the set of the real numbers $\\mathbb{R}$. For example, $x^2 = -1$ can be rearranged into $x = \\sqrt{-1}$, but the square root is not defined for negative numbers. To mitigate this, mathematicians introduced the concept of an [imaginary number](https://en.wikipedia.org/wiki/Imaginary_number) $\\textbf{i}$ that is *defined* as $\\textbf{i} = \\sqrt{-1}$ or often as the solution to the equation $\\textbf{i}^2 = -1$. So, the solution to $x = \\sqrt{-1}$ then becomes $x = \\textbf{i}$.\n", "\n", "If we generalize the example equation into $(mx-n)^2 = -1 \\implies x = \\frac{1}{m}(\\sqrt{-1} + n)$ where $m$ and $n$ are constants chosen from the reals $\\mathbb{R}$, then the solution to the equation comes in the form $x = a + b\\textbf{i}$, the sum of a real number and an imaginary number, with $a=\\frac{n}{m}$ and $b = \\frac{1}{m}$.\n", "\n", "Such \"compound\" numbers are called **[complex numbers](https://en.wikipedia.org/wiki/Complex_number)**, and the set of all such numbers is commonly denoted by $\\mathbb{C}$. The reals $\\mathbb{R}$ are a strict subset of $\\mathbb{C}$ with $b=0$. Further, $a$ is referred to as the **real part** and $b$ as the **imaginary part** of the complex number.\n", "\n", "Complex numbers are often visualized in a plane like below, where the real part is depicted on the x-axis and the imaginary part on the y-axis." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`complex` numbers are part of core Python. The simplest way to create one is to write an arithmetic expression with the literal `j` notation for $\\textbf{i}$. The `j` is commonly used in many engineering disciplines instead of the symbol $\\textbf{i}$ from math as $I$ in engineering more often than not means [electric current](https://en.wikipedia.org/wiki/Electric_current).\n", "\n", "For example, the answer to $x^2 = -1$ can be written in Python as `1j` like below. This creates a `complex` object with value `1j`. The same syntactic rules apply as with the above `e` notation: No spaces are allowed between the number and the `j`. The number may be any `int` or `float` literal; however, it is stored as a `float` internally. So, `complex` numbers suffer from the same imprecision as `float` numbers." ] }, { "cell_type": "code", "execution_count": 180, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "x = 1j" ] }, { "cell_type": "code", "execution_count": 181, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "140166567772848" ] }, "execution_count": 181, "metadata": {}, "output_type": "execute_result" } ], "source": [ "id(x)" ] }, { "cell_type": "code", "execution_count": 182, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "complex" ] }, "execution_count": 182, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(x)" ] }, { "cell_type": "code", "execution_count": 183, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "1j" ] }, "execution_count": 183, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "To verify that it solves the equation, let's raise it to the power of $2$." ] }, { "cell_type": "code", "execution_count": 184, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 184, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x ** 2 == -1" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Often, we write an expression of the form $a + b\\textbf{i}$." ] }, { "cell_type": "code", "execution_count": 185, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "(2+0.5j)" ] }, "execution_count": 185, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2 + 0.5j" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Alternatively, we may use the [complex()](https://docs.python.org/3/library/functions.html#complex) built-in: This takes two parameters where the second is optional and defaults to `0`. We may either call it with one or two arguments of any numeric type or a `str` object in the format of the previous code cell without any spaces." ] }, { "cell_type": "code", "execution_count": 186, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "(2+0.5j)" ] }, "execution_count": 186, "metadata": {}, "output_type": "execute_result" } ], "source": [ "complex(2, 0.5) # an integer and a float work just fine as arguments" ] }, { "cell_type": "code", "execution_count": 187, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "(2+0j)" ] }, "execution_count": 187, "metadata": {}, "output_type": "execute_result" } ], "source": [ "complex(2) # omitting the second argument sets the imaginary part to 0" ] }, { "cell_type": "code", "execution_count": 188, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "(2+0.5j)" ] }, "execution_count": 188, "metadata": {}, "output_type": "execute_result" } ], "source": [ "complex(Decimal(\"2.0\"), Fraction(1, 2)) # the arguments may be any numeric type" ] }, { "cell_type": "code", "execution_count": 189, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "(2+0.5j)" ] }, "execution_count": 189, "metadata": {}, "output_type": "execute_result" } ], "source": [ "complex(\"2+0.5j\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Arithmetic expressions work with `complex` numbers. They may be mixed with the other numeric types, and the result is always a `complex` number." ] }, { "cell_type": "code", "execution_count": 190, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "c1 = 1 + 2j\n", "c2 = 3 + 4j" ] }, { "cell_type": "code", "execution_count": 191, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "(4+6j)" ] }, "execution_count": 191, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c1 + c2" ] }, { "cell_type": "code", "execution_count": 192, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "(-2-2j)" ] }, "execution_count": 192, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c1 - c2" ] }, { "cell_type": "code", "execution_count": 193, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "(40+2j)" ] }, "execution_count": 193, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c1 + 39" ] }, { "cell_type": "code", "execution_count": 194, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "-4j" ] }, "execution_count": 194, "metadata": {}, "output_type": "execute_result" } ], "source": [ "3.0 - c2" ] }, { "cell_type": "code", "execution_count": 195, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "(5+10j)" ] }, "execution_count": 195, "metadata": {}, "output_type": "execute_result" } ], "source": [ "5 * c1" ] }, { "cell_type": "code", "execution_count": 196, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ "(0.5+0.6666666666666666j)" ] }, "execution_count": 196, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c2 / 6" ] }, { "cell_type": "code", "execution_count": 197, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "(-5+10j)" ] }, "execution_count": 197, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c1 * c2" ] }, { "cell_type": "code", "execution_count": 198, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "(0.44+0.08j)" ] }, "execution_count": 198, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c1 / c2" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The absolute value of a `complex` number $x$ is defined with the Pythagorean Theorem where $\\lVert x \\rVert = \\sqrt{a^2 + b^2}$ and $a$ and $b$ are the real and imaginary parts. The [abs()](https://docs.python.org/3/library/functions.html#abs) built-in function implements that in Python." ] }, { "cell_type": "code", "execution_count": 199, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "5.0" ] }, "execution_count": 199, "metadata": {}, "output_type": "execute_result" } ], "source": [ "abs(3 + 4j)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "A `complex` number comes with two **attributes** `real` and `imag` that return the two parts as `float` objects on their own." ] }, { "cell_type": "code", "execution_count": 200, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 200, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c1.real" ] }, { "cell_type": "code", "execution_count": 201, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "2.0" ] }, "execution_count": 201, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c1.imag" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Also, a `conjugate()` method is bound to every `complex` object. The [complex conjugate](https://en.wikipedia.org/wiki/Complex_conjugate) is defined to be the complex number with identical real part but an imaginary part reversed in sign." ] }, { "cell_type": "code", "execution_count": 202, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "(1-2j)" ] }, "execution_count": 202, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c1.conjugate()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The [cmath](https://docs.python.org/3/library/cmath.html) module in the [standard library](https://docs.python.org/3/library/index.html) implements many of the functions from the [math](https://docs.python.org/3/library/math.html) module such that they work with complex numbers." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## The Numerical Tower" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Analogous to the discussion of *containers* and *iterables* in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#Containers-vs.-Iterables), we contrast the *concrete* numeric data types in this chapter with the *abstract* ideas behind [numbers in mathematics](https://en.wikipedia.org/wiki/Number).\n", "\n", "The figure below summarizes five *major* sets of [numbers in mathematics](https://en.wikipedia.org/wiki/Number) as we know them from high school:\n", "\n", "- $\\mathbb{N}$: [Natural numbers](https://en.wikipedia.org/wiki/Natural_number) are all non-negative count numbers, e.g., $0, 1, 2, ...$\n", "- $\\mathbb{Z}$: [Integers](https://en.wikipedia.org/wiki/Integer) are all numbers *without* a fractional component, e.g., $-1, 0, 1, ...$\n", "- $\\mathbb{Q}$: [Rational numbers](https://en.wikipedia.org/wiki/Rational_number) are all numbers that can be expressed as a quotient of two integers, e.g., $-\\frac{1}{2}, 0, \\frac{1}{2}, ...$\n", "- $\\mathbb{R}$: [Real numbers](https://en.wikipedia.org/wiki/Real_number) are all numbers that can be represented as a distance along a line, and negative means \"reversed,\" e.g., $\\sqrt{2}, \\pi, \\text{e}, ...$\n", "- $\\mathbb{C}$: [Complex numbers](https://en.wikipedia.org/wiki/Complex_number) are all numbers of the form $a + b\\textbf{i}$ where $a$ and $b$ are real numbers and $\\textbf{i}$ is the [imaginary number](https://en.wikipedia.org/wiki/Imaginary_number), e.g., $0, \\textbf{i}, 1 + \\textbf{i}, ...$\n", "\n", "In the listed order, the five sets are perfect subsets of the respective following sets, and $\\mathbb{C}$ is the largest set (cf., the figure below illustrates that observation as well). To be precise, all sets are infinite, but they still have a different number of elements." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The data types introduced in this chapter are all *imperfect* models of *abstract* mathematical ideas.\n", "\n", "The `int` and `Fraction` types are the models \"closest\" to the idea they implement: Whereas $\\mathbb{Z}$ and $\\mathbb{Q}$ are, by definition, infinite, every computer runs out of bits when representing sufficiently large integers or fractions with a sufficiently large number of decimals. However, within a system-dependent range, we can model an integer or fraction without any loss in precision.\n", "\n", "For the other types, in particular, the `float` type, the implications of their imprecision are discussed in detail above.\n", "\n", "The abstract concepts behind the four outer-most mathematical sets are formalized in Python since [PEP 3141](https://www.python.org/dev/peps/pep-3141/) in 2007. The [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines what programmers call the **[numerical tower](https://en.wikipedia.org/wiki/Numerical_tower)**, a collection of five **[abstract data types](https://en.wikipedia.org/wiki/Abstract_data_type)**, or **abstract base classes** (ABCs) as they are called in Python jargon:\n", "\n", "- `Number`: \"any number\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Number))\n", "- `Complex`: \"all complex numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Complex))\n", "- `Real`: \"all real numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real))\n", "- `Rational`: \"all rational numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Rational))\n", "- `Integral`: \"all integers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Integral))" ] }, { "cell_type": "code", "execution_count": 203, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "import numbers" ] }, { "cell_type": "code", "execution_count": 204, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "['ABCMeta',\n", " 'Complex',\n", " 'Integral',\n", " 'Number',\n", " 'Rational',\n", " 'Real',\n", " '__all__',\n", " '__builtins__',\n", " '__cached__',\n", " '__doc__',\n", " '__file__',\n", " '__loader__',\n", " '__name__',\n", " '__package__',\n", " '__spec__',\n", " 'abstractmethod']" ] }, "execution_count": 204, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(numbers)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "As a reminder, the built-in [help()](https://docs.python.org/3/library/functions.html#help) function is always our friend.\n", "\n", "The ABCs' docstrings are unsurprisingly similar to the corresponding data types' docstrings. For now, let's not worry about the dunder-style names in the docstrings.\n", "\n", "For example, both `numbers.Complex` and `complex` list the `imag` and `real` attributes shown above." ] }, { "cell_type": "code", "execution_count": 205, "metadata": { "scrolled": true, "slideshow": { "slide_type": "skip" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on class Complex in module numbers:\n", "\n", "class Complex(Number)\n", " | Complex defines the operations that work on the builtin complex type.\n", " | \n", " | In short, those are: a conversion to complex, .real, .imag, +, -,\n", " | *, /, abs(), .conjugate, ==, and !=.\n", " | \n", " | If it is given heterogeneous arguments, and doesn't have special\n", " | knowledge about them, it should fall back to the builtin complex\n", " | type as described below.\n", " | \n", " | Method resolution order:\n", " | Complex\n", " | Number\n", " | builtins.object\n", " | \n", " | Methods defined here:\n", " | \n", " | __abs__(self)\n", " | Returns the Real distance from 0. Called for abs(self).\n", " | \n", " | __add__(self, other)\n", " | self + other\n", " | \n", " | __bool__(self)\n", " | True if self != 0. Called for bool(self).\n", " | \n", " | __complex__(self)\n", " | Return a builtin complex instance. Called for complex(self).\n", " | \n", " | __eq__(self, other)\n", " | self == other\n", " | \n", " | __mul__(self, other)\n", " | self * other\n", " | \n", " | __neg__(self)\n", " | -self\n", " | \n", " | __pos__(self)\n", " | +self\n", " | \n", " | __pow__(self, exponent)\n", " | self**exponent; should promote to float or complex when necessary.\n", " | \n", " | __radd__(self, other)\n", " | other + self\n", " | \n", " | __rmul__(self, other)\n", " | other * self\n", " | \n", " | __rpow__(self, base)\n", " | base ** self\n", " | \n", " | __rsub__(self, other)\n", " | other - self\n", " | \n", " | __rtruediv__(self, other)\n", " | other / self\n", " | \n", " | __sub__(self, other)\n", " | self - other\n", " | \n", " | __truediv__(self, other)\n", " | self / other: Should promote to float when necessary.\n", " | \n", " | conjugate(self)\n", " | (x+y*i).conjugate() returns (x-y*i).\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors defined here:\n", " | \n", " | imag\n", " | Retrieve the imaginary component of this number.\n", " | \n", " | This should subclass Real.\n", " | \n", " | real\n", " | Retrieve the real component of this number.\n", " | \n", " | This should subclass Real.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes defined here:\n", " | \n", " | __abstractmethods__ = frozenset({'__abs__', '__add__', '__complex__', ...\n", " | \n", " | __hash__ = None\n", "\n" ] } ], "source": [ "help(numbers.Complex)" ] }, { "cell_type": "code", "execution_count": 206, "metadata": { "slideshow": { "slide_type": "skip" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on class complex in module builtins:\n", "\n", "class complex(object)\n", " | complex(real=0, imag=0)\n", " | \n", " | Create a complex number from a real part and an optional imaginary part.\n", " | \n", " | This is equivalent to (real + imag*1j) where imag defaults to 0.\n", " | \n", " | Methods defined here:\n", " | \n", " | __abs__(self, /)\n", " | abs(self)\n", " | \n", " | __add__(self, value, /)\n", " | Return self+value.\n", " | \n", " | __bool__(self, /)\n", " | self != 0\n", " | \n", " | __divmod__(self, value, /)\n", " | Return divmod(self, value).\n", " | \n", " | __eq__(self, value, /)\n", " | Return self==value.\n", " | \n", " | __float__(self, /)\n", " | float(self)\n", " | \n", " | __floordiv__(self, value, /)\n", " | Return self//value.\n", " | \n", " | __format__(...)\n", " | complex.__format__() -> str\n", " | \n", " | Convert to a string according to format_spec.\n", " | \n", " | __ge__(self, value, /)\n", " | Return self>=value.\n", " | \n", " | __getattribute__(self, name, /)\n", " | Return getattr(self, name).\n", " | \n", " | __getnewargs__(...)\n", " | \n", " | __gt__(self, value, /)\n", " | Return self>value.\n", " | \n", " | __hash__(self, /)\n", " | Return hash(self).\n", " | \n", " | __int__(self, /)\n", " | int(self)\n", " | \n", " | __le__(self, value, /)\n", " | Return self<=value.\n", " | \n", " | __lt__(self, value, /)\n", " | Return self complex\n", " | \n", " | Return the complex conjugate of its argument. (3-4j).conjugate() == 3+4j.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Static methods defined here:\n", " | \n", " | __new__(*args, **kwargs) from builtins.type\n", " | Create and return a new object. See help(type) for accurate signature.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors defined here:\n", " | \n", " | imag\n", " | the imaginary part of a complex number\n", " | \n", " | real\n", " | the real part of a complex number\n", "\n" ] } ], "source": [ "help(complex)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The primary purpose of ABCs is to classify the *concrete* data types and standardize how they behave.\n", "\n", "For, example, as all numeric data types are `Complex` numbers in the abstract sense, they all work with the built-in [abs()](https://docs.python.org/3/library/functions.html#abs) function (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Complex)). While it is intuitively clear what the [absolute value](https://en.wikipedia.org/wiki/Absolute_value) (i.e., \"distance\" to $0$) of an integer, a fraction, or any real number is, [abs()](https://docs.python.org/3/library/functions.html#abs) calculates the equivalent of that for complex numbers. That concept is called the [magnitude](https://en.wikipedia.org/wiki/Magnitude_%28mathematics%29) of a number, and is really a *generalization* of the absolute value." ] }, { "cell_type": "code", "execution_count": 207, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "42" ] }, "execution_count": 207, "metadata": {}, "output_type": "execute_result" } ], "source": [ "abs(-42)" ] }, { "cell_type": "code", "execution_count": 208, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "Decimal('0.1')" ] }, "execution_count": 208, "metadata": {}, "output_type": "execute_result" } ], "source": [ "abs(Decimal(\"-0.1\"))" ] }, { "cell_type": "code", "execution_count": 209, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "5.0" ] }, "execution_count": 209, "metadata": {}, "output_type": "execute_result" } ], "source": [ "abs(4 - 3j)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "On the contrary, only `Real` numbers in the abstract sense may be rounded with the built-in [round()](https://docs.python.org/3/library/functions.html#round) function." ] }, { "cell_type": "code", "execution_count": 210, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "42" ] }, "execution_count": 210, "metadata": {}, "output_type": "execute_result" } ], "source": [ "round(42.1)" ] }, { "cell_type": "code", "execution_count": 211, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 211, "metadata": {}, "output_type": "execute_result" } ], "source": [ "round(Decimal(\"0.1\"))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`Complex` numbers are two-dimensional. So, rounding makes no sense and raises a `TypeError`." ] }, { "cell_type": "code", "execution_count": 212, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "ename": "TypeError", "evalue": "type complex doesn't define __round__ method", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mround\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m3j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: type complex doesn't define __round__ method" ] } ], "source": [ "round(4 + 3j)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Knowing what ABCs a numeric type adheres to, is not only important in the context of built-ins. The [trunc()](https://docs.python.org/3/library/math.html#math.trunc) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html), for example, only works with `Real` types (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real))." ] }, { "cell_type": "code", "execution_count": 213, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "import math" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "[trunc()](https://docs.python.org/3/library/math.html#math.trunc) cuts off a number's decimals." ] }, { "cell_type": "code", "execution_count": 214, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 214, "metadata": {}, "output_type": "execute_result" } ], "source": [ "math.trunc(9 / 10)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "A `Complex` number leads to a `TypeError`." ] }, { "cell_type": "code", "execution_count": 215, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "ename": "TypeError", "evalue": "type complex doesn't define __trunc__ method", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0.9\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: type complex doesn't define __trunc__ method" ] } ], "source": [ "math.trunc(0.9 + 1j)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Another way to use ABCs is in place of a *concrete* data type.\n", "\n", "For example, we may pass them as arguments to the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function and check in which of the five mathematical sets the object `1 / 10` is." ] }, { "cell_type": "code", "execution_count": 216, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 216, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(1 / 10, float) # we know that from before" ] }, { "cell_type": "code", "execution_count": 217, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 217, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(1 / 10, numbers.Number) # a float object is a Number in the abstract sense" ] }, { "cell_type": "code", "execution_count": 218, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 218, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(1 / 10, numbers.Complex) # float objects are always also Complex numbers" ] }, { "cell_type": "code", "execution_count": 219, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 219, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(1 / 10, numbers.Real) # a float object's purpose is to model a Real number" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Due to the `float` type's inherent imprecision, `1 / 10` is *not* a rational number." ] }, { "cell_type": "code", "execution_count": 220, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 220, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(1 / 10, numbers.Rational) # the type of `1 / 10` is what is important, not its value" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "The `Fraction` type qualifies as a rational number; however, the `Decimal` type does not." ] }, { "cell_type": "code", "execution_count": 221, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 221, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(Fraction(1, 10), numbers.Rational)" ] }, { "cell_type": "code", "execution_count": 222, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 222, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(Decimal(\"0.1\"), numbers.Rational)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Goose Typing" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Replacing *concrete* data types with ABCs is particularly valuable in the context of input validation: The revised version of the `factorial()` function below allows its user to take advantage of *duck typing*: If a real but non-integer argument `n` is passed in, `factorial()` tries to cast `n` as an `int` object with [math.trunc()](https://docs.python.org/3/library/math.html#math.trunc).\n", "\n", "Two popular and distinguished Pythonistas, [Luciano Ramalho](https://github.com/ramalho) and [Alex Martelli](https://en.wikipedia.org/wiki/Alex_Martelli), coin the term **goose typing** to specifically mean using the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function with an ABC (cf., Chapter 11 in this [book](https://www.amazon.com/Fluent-Python-Concise-Effective-Programming/dp/1491946008) or this [summary](https://dgkim5360.github.io/blog/python/2017/07/duck-typing-vs-goose-typing-pythonic-interfaces/) thereof)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "#### Example: [Factorial](https://en.wikipedia.org/wiki/Factorial) (revisited)" ] }, { "cell_type": "code", "execution_count": 223, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "def factorial(n, *, strict=True):\n", " \"\"\"Calculate the factorial of a number.\n", "\n", " Args:\n", " n (int): number to calculate the factorial for; must be positive\n", " strict (bool): if n must not contain decimals; defaults to True\n", "\n", " Returns:\n", " factorial (int)\n", "\n", " Raises:\n", " TypeError: if n is not an integer or cannot be cast as such\n", " ValueError: if n is negative\n", " \"\"\"\n", " if not isinstance(n, numbers.Integral):\n", " if isinstance(n, numbers.Real):\n", " if n != math.trunc(n) and strict:\n", " raise TypeError(\"n is not an integer-like value; it has decimals\")\n", " n = math.trunc(n)\n", " else:\n", " raise TypeError(\"Factorial is only defined for integers\")\n", "\n", " if n < 0:\n", " raise ValueError(\"Factorial is not defined for negative integers\")\n", " elif n == 0: # = base case\n", " return 1\n", " return n * factorial(n - 1)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "`factorial()` works as before, but now also accepts, for example, `float` numbers." ] }, { "cell_type": "code", "execution_count": 224, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 224, "metadata": {}, "output_type": "execute_result" } ], "source": [ "factorial(0)" ] }, { "cell_type": "code", "execution_count": 225, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 225, "metadata": {}, "output_type": "execute_result" } ], "source": [ "factorial(3)" ] }, { "cell_type": "code", "execution_count": 226, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 226, "metadata": {}, "output_type": "execute_result" } ], "source": [ "factorial(3.0)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "With the keyword-only argument `strict`, we can control whether or not a passed in `float` object may come with decimals that are then truncated. By default, this is not allowed and results in a `TypeError`." ] }, { "cell_type": "code", "execution_count": 227, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "ename": "TypeError", "evalue": "n is not an integer-like value; it has decimals", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not an integer-like value; it has decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: n is not an integer-like value; it has decimals" ] } ], "source": [ "factorial(3.1)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "In non-strict mode, the passed in `3.1` is truncated into `3` resulting in a factorial of `6`." ] }, { "cell_type": "code", "execution_count": 228, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 228, "metadata": {}, "output_type": "execute_result" } ], "source": [ "factorial(3.1, strict=False)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "For `complex` numbers, `factorial()` still raises a `TypeError` because they are neither an `Integral` nor a `Real` number." ] }, { "cell_type": "code", "execution_count": 229, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "ename": "TypeError", "evalue": "Factorial is only defined for integers", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" ] } ], "source": [ "factorial(1 + 2j)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "## TL;DR" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "There exist three numeric types in core Python:\n", "- `int`: a near-perfect model for whole numbers (i.e., $\\mathbb{Z}$); inherently precise\n", "- `float`: the \"gold\" standard to approximate real numbers (i.e., $\\mathbb{R}$); inherently imprecise\n", "- `complex`: layer on top of the `float` type to approximate complex numbers (i.e., $\\mathbb{C}$); inherently imprecise\n", "\n", "Furthermore, the [standard library](https://docs.python.org/3/library/index.html) provides two more types that can be used as substitutes for the `float` type:\n", "- `Decimal`: similar to `float` but allows customizing the precision; still inherently imprecise\n", "- `Fraction`: a near-perfect model for rational numbers (i.e., $\\mathbb{Q}$); built on top of the `int` type and therefore inherently precise\n", "\n", "The *important* takeaways for the data science practitioner are:\n", "\n", "1. **Do not mix** precise and imprecise data types, and\n", "2. actively expect `nan` results when working with `float` numbers as there are no **loud failures**.\n", "\n", "The **numerical tower** is Python's way of implementing various **abstract** ideas of what numbers are in mathematics." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.4" }, "livereveal": { "auto_select": "code", "auto_select_fragment": true, "scroll": true, "theme": "serif" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": false, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": { "height": "calc(100% - 180px)", "left": "10px", "top": "150px", "width": "384px" }, "toc_section_display": false, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 4 }