numbers

Read their own future sections for containers:

The Control Flow chapter has already covered Booleans.

- snippet.juliarepl
julia> pkgchk

**(****[****"**julia**"**=> v**"**1.0.2**"****,****"**Missings**"**=> v**"**0.3.1**"****,****"**Formatting**"**=> v**"**0.3.4**"****,****"**CategoricalArrays**"**=> v**"**0.4.0**"****]****)**

- snippet.juliarepl
julia> typeof

**(**2**)**Int64 julia> typeof**(**3.0**)**Float64

Use 'const' (the constant modifier) generously, especially for global variables that you want to (ab-)use in function. This improves both bug-detection and execution speed, often dramatically. Or avoid globals altogether.

IMHO, primitive native-CPU type objects are best. This improves bug-detection, memory footprint, and speed.

Use broader types only when there is a clear need (such as in function arguments, in which you *really* want the function to have broader and more generic reusable functionality). This can be the case, e.g., if you want to feed the function to an automatic differentiator (see Andreas' note below.)

More generic numeric types—such as `Real`

(which can be either `Int*`

or `Float*`

); and `Number`

(which can be either `Real`

or `Complex`

)—are almost always useless overkill in specific scientific application programming. The `Any`

type is even broader, so avoid it like the undead dead. (The exception to the 'do not use Any' rule is printing, where `Any`

printing can indeed be quite useful.)

Unfortunately, global variables cannot have types in Julia 1.0 yet.

My advice here is controversial. Andreas Noack, for example, points out that stricter signatures might be more informative about the function but they won't make the function faster per se. The method will become specialized for the specific input types regardless of signature. An important counter example to your [preference for broader types] in scientific computations is automatic differentiation. This is a very powerful computational method. It should be used much more widely than it currently is. The main requirement is that one can pass special number types through and restricting to e.g. `Float64`

is therefore typically not a good idea. Annotating the variables also will not give any speed up. It might reveal a type instability in a function but we usually recommend *not* to annotate variables with types.

This is a common beginner's mistake.

statement | result | explanation |
---|---|---|

`v= Float64` | a type | desired? v is now an alias for the Float64 type. `x=v(NaN)` now declares a variable. |

`v= Float64(NaN)` | an initialized variable | v is a now a variable initialized with NaN. |

Inquiring explained how you can learn more about both types and variables (use `typeof`

and `dump()`

).

Variables (object instances) in Julia are always typed. (A type can be generic, like a `Number`

[which could be an nteger or a float] or an `Any`

[which can indeed hold anything]). However, Julia makes it easier on the user by often infering a “default” types. `x= 12`

will make `x`

of type `Int64`

, `x= 12.0`

a `Float64`

, `x= 'c'`

a `Char`

, `x= "s"`

a `String`

, and so on. The two most commonly used (numeric) types in julia are the defaults

`Int`

(usually identical to`Int64`

).`Float64`

(there is no generic`Float`

).

- snippet.juliarepl
julia> typeof

**(**1**)****,**typeof**(**1.0**)****(**Int64**,**Float64**)**

There are bigger and smaller types, as well as unsigned integer types. The exact number hierarchy is as follows:

- snippet.text
[download only julia statements] Number Complex Real AbstractFloat: BigFloat Float64 Float32 Float16 Integer BigInt Bool Signed: Int128 Int64 Int32 Int16 Int8 Unsigned: UInt128 Int64 UInt32 UInt16 UInt8 Irrational Rational

- Just noted, there is no
`Float`

type, and the`Int`

type aliases for`Int64`

on all modern 64-bit CPUs. See also Inquiring for`subtype()`

and`supertype()`

functions. However… - Function arguments that want to work with Integers and Floats use
`Real`

. Function arguments that work only with Floats use`AbstractFloat`

. Function arguments that work only with Ints use`Integer`

. - Typed variables can be uninitizialized (contain garbage), but they usually cannot have empty (or missing or Null) content choice—to facilitate such requires explicit retyping of the variable to allow an empty or
`missing`

variable. The Missing*type*with missing*value*(note the capitalization) will be covered in detail in chapter Missings. When missing values are involved, dealing with types becomes messier.

Most users will be on later-model Intel CPUs. On a Xeon i7-7700, I ran a simple test program:

- snippet.text
[download only julia statements] using BenchmarkTools M= 1_000_000 T= Int16; println(T); @btime begin v= Vector{T}(rand(5:999999, M)); v= v+v.*v-mod.(v, v-3); end;

The following table is suggestive about relative speeds of using different types:

Type | Int8 | Int16 | Int32 | Int64 | Int128 | Float16 | Float32 | Float64 | BigFloat |
---|---|---|---|---|---|---|---|---|---|

Time | 1.0 | 1.0 | 1.0 | 1.2 | 1.9 | 2.7 | 0.9 | 1.0 | 120.0 |

- Not Shown: Unsigned types are often 10-20% slower.
- 32-bit numerics are Intel's sweet spot.
- Int128 is notably slower among Ints.
- Float32 is fastest, but Float64 is “only” 50% slower.
- Float16 and BigFloat are best avoided unless absolutely necessary. For all but the most trivial calculations, it is faster when a program converts Float16 to Float32, calculates in Float32, and then converts back (compared to calculating natively in Float16). (Here, this would take 31ms.)
- If you need both speed and BigFloat precision, either find a new computer architecture or a priest.

Please see the chapter on Missings. The Missings chapter also discusses NaN, which are not missings, but are part of the ordinary IEEE Float definitions. (Roughly speaking, using missing raises calculation time by a factor of 3. Using NaN has no speed penalty.)

Already discussed in Inquiring, `typeof()`

is the key function. The content `eltype`

(“element-type”) works for both scalars and for the *contents* of arrays:

- snippet.juliarepl
julia> typeof

**(**2**)**## scalar test Int64 julia> typeof**(****[**2**,**3**]****)**## type of whole vector Array**{**Int64**,**1**}**julia> eltype**(**2.0**)**## element type for a scalar Float64 julia> eltype**(****[**2**,**3**]****)**## element types for arrays Int64

- PS: Vectors and arrays will be explained in Arrays:

- snippet.juliarepl
julia> eltype

**(**1**)**<: Number true julia> eltype**(**2**)**<: Real true julia> eltype**(**1.0**)**<: Int false

**Warning**: This does not work when the type is a union that includes support for missings.

- snippet.juliarepl
julia> eltype

**(****[**1**,**missing**]****)**Union**{**Missing**,**Int64**}**julia> eltype**(****[**1**,**missing**]****)**<: Int64 false

Instead, you can test for a Union-with-missing type or simply eliminate the missing first:

- snippet.juliarepl
julia> eltype

**(****[**1**,**missing**]****)**<: Union**{**Missing**,**Int64**}**## standard test true julia> eltype**(**collect**(**skipmissing**(****[**1**,**missing**,**3**]****)****)****)**<: Int64 ## or remove missings first true

Use `==`

for value comparisons. Use `===`

for value plus type plus memory-location comparisons:

- snippet.juliarepl
julia> Int8

**(**2**)**== Int32**(**2**)**## compare value only true julia> x= 3; y= 3;**(**x === y**)**## quasi-dereferenced variables**'**contents are type and value identical true julia> Int8**(**2**)**=== Int32**(**2**)**## but type here differs false julia> x=**[**2**]**; y=**[**2**]**;**(**x === y**)**## and pointed-to storage locations are different false

- PS: Julia also allows chained comparisons, like `1 < x <= 2 `

- snippet.juliarepl
julia> convert

**(**Float32**,**1**)**## Int to Float32 1.0f0 julia> Char**(**round**(**Int**,**12.4**)****)**## Float64**(**12.4**)**to Int**(**12**)**to Char**(**\f**)****'**\f**'**: ASCII/Unicode U+000c**(**category Cc: Other**,**control**)**julia> oftype**(**12.0**,**1**)**## convert arg2**(**1**)**to type of arg1**(**12.0**)****,**i.e.**,**Int to Float64 1.0 julia> promote**(**1**,**2.5**,**true**)**## promotion = make them all the same**(**lcd**)**type**(**1.0**,**2.5**,**1.0**)**

- snippet.juliarepl
julia> convert

**(**Vector**{**Float16**}****,****[**1**,**2**]****)**## Vector**{**Int**}**to Vector**{**Float16**}**2-element Array**{**Float16**,**1**}**: 1.0 2.0 julia> String**(****[****'**a**'****,****'**b**'****]****)**## Preview: smart String converter of Char. see Strings chapter.**"**ab**"**julia> string**(****[****'**a**'****,****'**b**'****]****)**## lowercase = just stringify**"****[****'**a**'****,****'**b**'****]****"**

- Long numbers can be entered with underscores for visual help:
`1_000_000.123_456`

. - Converting a string to a Float64 is
`parse(Float64, "12.2")`

, not`Float64("12.2")`

.

- snippet.juliarepl
julia> typemax

**(**Float64**)**## largest inf Inf julia> floatmax**(**Float64**)**## largest non-inf float...richer than Jeff Bezos 1.7976931348623157e308 julia> maxintfloat**(**Float64**)**## largest int representable in Float64 without loss 9.007199254740992e15 julia> typemin**(**Int16**)**## most negative –32768

The smallest (tiniest) value above zero that is representable,

`eps(T)`

is `eps(one(T))`

i.e. the distance bewtween floats at one aka machine epsilon. The smallest value above zero is `eps(zero(T))`

which for `Float64`

is `5.0e-324`

- snippet.juliarepl
julia> eps

**(**Float64**)**2.220446049250313e–16

- Obscure:
`nextfloat`

gives the next representable float.`RoundNearest`

can inform IEEE-754 operation.

- snippet.juliarepl
julia> isfinite.

**(****(**NaN**,**Inf**,**-Inf**,**0.0**)****)****(**false**,**false**,**false**,**true**)**julia> isnan.**(****(**NaN**,**Inf**,**-Inf**,**0.0**)****)****(**true**,**false**,**false**,**false**)**julia> isinf.**(****(**NaN**,**Inf**,**-Inf**,**0.0**)****)****(**false**,**true**,**true**,**false**)**

See also the chapter on Missings and NaN.

To round a `Float64`

to `Int`

:

- snippet.juliarepl
julia> convert

**(**Int**,**15.0**)**## 15.0 can be cleanly converted 15 julia> convert**(**Int**,**15.3**)**## 15.3 cannot be cleanly converted ERROR: InexactError: Int64**(**Int64**,**15.3**)**Stacktrace:

round | to nearest integer |
---|---|

floor | towards -Inf |

ceil | towards +Inf |

trunc | towards 0 |

- snippet.juliarepl
julia> round

**(**Int**,**15.3**)**## you must decide on your intent 15 julia> typeof**(**ans**)**## Julia was smart enough to change the type Int64 julia> convert**(**Vector**{**Int**}****,****[**trunc**(**12.2**)****,**floor**(**12.2**)****,**trunc**(**–12.3**)****,**floor**(**–12.3**)****]****)**4-element Array**{**Int64**,**1**}**: 12 12 –12 –13

- snippet.juliarepl
julia> modf

**(**12.312**)**## fraction comes first**,**then integer part**(**but still a Float**)****(**0.3119999999999994**,**12.0**)**

- snippet.juliarepl
julia> round

**(**123.4567; digits=2**)**## same operation as Float->Int 123.46

- There are also further such functions for division/modules.

- snippet.juliarepl
julia> round.

**(****[**123.456789**,**1.23456**,**0.0123456**,**0.000123456**]**; sigdigits=2**)**4-element Array**{**Float64**,**1**}**: 120.0 1.2 0.012 0.00012

- snippet.juliarepl
julia> mround

**(**v::AbstractFloat**,**nearest::AbstractFloat**)**= round**(**v/nearest**)***nearest; julia> mround**(**24.4324245**,**0.05**)**## uh oh. we need to do better 24.450000000000003 julia> mround**(**v::AbstractFloat**,**nearest::AbstractFloat**)**= round**(**round**(**v/nearest**)***nearest; digits= Int**(**1+abs**(**floor**(**log10**(**nearest**)****)****)****)****)**; julia> mround**(**24.432**,**0.05**)**## ugly function**,**but does the job 24.45

The usual caveats on floating-point precision apply:

- snippet.juliarepl
julia> isequal

**(****(**sqrt**(**5**)****)**^2**,**5**)**false

because

- snippet.juliarepl
julia> sqrt

**(**5**)**^2 5.000000000000001

Instead, use `isapprox`

, which is in UTF the `≈`

operator:

- snippet.juliarepl
julia> isapprox

**(****(**sqrt**(**5**)****)**^2**,**5.0**)**true julia> isapprox**(****(**sqrt**(**5**)****)**^2**,**5.0**,**atol=0.0001**)**true

- snippet.juliarepl
julia> using Formatting julia> sprintf1

**(****"**%**'**d**"****,**1_000_000**)****"**1**,**000**,**000**"**julia> sprintf1**(****"**%**'**f**"****,**1_000_000.0**)****"**1**,**000**,**000.000000**"**julia> format**(**100_000.0**,**width=10**,**signed=true**,**leftjustified=true**)**## many more options**"**+100000**"**

- this also works for arrays and ranges:
`sprintf1.("%.3f", 1:3)`

and`sprintf1.("%.3f", [ 1 2 ; 3 4])`

C-style printf and sprintf work as macros, so they require '@' prefixes:

- snippet.juliarepl
julia> using Printf julia> @printf

**(****"**%12.5f**"****,**pi**)**3.14159

- this also works for arrays and ranges:
`sprintf1.("%.3f", 1:3)`

and`sprintf1.("%.3f", [ 1 2 ; 3 4])`

Most standard mathematical functions (e.g., exp, sin, etc.) work as expected. Some less common but very useful functions

Function | Explanation |
---|---|

`abs2(8)` | the square (64) |

`cbrt(8)` | the cuberoot (2.0), type Float! |

`expm1(0)` | `=exp(0)-1` , here 0 |

`log1p(0)` | `=log(1+0)` , here 0 |

`log10(100)` | log 10, here 2.0 |

`sinpi(0.5)` | `=sin(pi*0.5)` , here 1.0, better accuracy |

`cospi(0.25)` | `=cos(pi*0.25)` , here 0.707… |

`sind(270)` | `= -1.0` input is in radians |

Many other useful functions are now defined in SpecialFunctions.jl. For example,

Function | Explanation |
---|---|

`erf(x)` | error function |

`gamma(x)` | gamma function |

`lgamma(x)` | log gamma function |

`lfact(x)` | log factorial (log x!) |

`beta(x,y)` | beta function |

`lbeta(x,y)` | log beta function |

There are '*=' operators that do '*' and then assign the result, such as `x+=1`

which adds 1 to x.

- Long numbers can be entered with underscores for visual help:
`1_000_000`

. - Integer overflows are not trapped.
`2^64`

gives zero without warning. - Converting string to int is
`parse("12")`

, not`Int("12")`

- snippet.juliarepl
julia> 10/7 ## note automatic promotion to float in division 1.4285714285714286 julia> trunc

**(**10/7**)**## trunc of int division**,**still results in float 1.0 julia> div**(**10**,**7**)**## true integer division with integer result 1 julia> rem**(**10**,**7**)**## modulus of division 3 julia> 10%7 ## same modulus 3 julia> divrem**(**10**,**7**)**## int division and modulus**(**1**,**3**)**julia> 10//7 + 1 ## // maintains fractions 17//7

- snippet.juliarepl
julia>

**(**0/0**,**1.0/0.0**,**1/0**,**1//0**)**## converts to Float with IEEE special values**(**NaN**,**Inf**,**Inf**,**1//0**)**julia> mod**(**1**,**0**)**## uh oh: no IEEE ERROR: DivideError: integer division error Stacktrace: julia> 1%0 ## uh oh: no IEEE ERROR: DivideError: integer division error Stacktrace:

You may or may not want to trap these exceptions.

- snippet.juliarepl
julia> string

**(**29; base=5**)****"**104**"**julia> println**(**digits**(**29**,**base=5**)****)****[**4**,**0**,**1**]**julia> println**(**reverse**(**digits**(**29**,**base=5**,**pad=6**)****)****)**## padding to 6 digits**[**0**,**0**,**0**,**1**,**0**,**4**]**julia> string**(**253; base=2**)****"**11111101**"**

Julia has faster `oct()`

and `hex()`

functions built in.

- snippet.juliarepl
julia> const x= 3; 2x 6 julia> 2^2x ## warning: not 4*3=12

**,**but 2^6. 64

An iterator is like a range of integers, typically used to index arrays, dictionaries, or other objects. Thus, they are mostly discussed in the Arrays Vector chapter.

- See also Combinatorics.jl.

- snippet.juliarepl
julia> for i=0:

**(**2^3–1**)**; println**(**bitstring**(**i**)****[****(**end–2**)**:end**]****)**; end#for ## 3 items**,**able to take 2 values**(**0 or 1**)**000 001 010 011 100 101 110 111 julia> for i=0:7; println**(**digits**(**i; base=2**,**pad=0**)****)**; end#for ## here**,**trailing zeros are removed Int64**[****]****[**1**]****[**0**,**1**]****[**1**,**1**]****[**0**,**0**,**1**]****[**1**,**0**,**1**]****[**0**,**1**,**1**]****[**1**,**1**,**1**]**

- snippet.juliarepl
julia> function permutations

**(**v::Vector**{**T**}****)**where T <: Any nbase= length**(**v**)**results= Vector**{**Vector**{**T**}****}****(**undef**,**0**)**for it=0:**(**nbase^nbase–1**)**push!**(**results**,**v**[**digits**(**it; base=nbase**,**pad=nbase**)**.+ 1**]****)**end#for results end#function## permutations**(**generic function with 1 method**)**julia> permutations**(****[****'**a**'****,****'**b**'****,****'**c**'****]****)**## or try**[****"**apple**"****,****"**strawberry**"****,****"**orange**"****]**27-element Array**{**Array**{**Char**,**1**}****,**1**}**:**[****'**a**'****,****'**a**'****,****'**a**'****]****[****'**b**'****,****'**a**'****,****'**a**'****]****[****'**c**'****,****'**a**'****,****'**a**'****]****[****'**a**'****,****'**b**'****,****'**a**'****]****[****'**b**'****,****'**b**'****,****'**a**'****]****[****'**c**'****,****'**b**'****,****'**a**'****]****[****'**a**'****,****'**c**'****,****'**a**'****]****[****'**b**'****,****'**c**'****,****'**a**'****]****[****'**c**'****,****'**c**'****,****'**a**'****]****[****'**a**'****,****'**a**'****,****'**b**'****]**⋮**[****'**a**'****,****'**a**'****,****'**c**'****]****[****'**b**'****,****'**a**'****,****'**c**'****]****[****'**c**'****,****'**a**'****,****'**c**'****]****[****'**a**'****,****'**b**'****,****'**c**'****]****[****'**b**'****,****'**b**'****,****'**c**'****]****[****'**c**'****,****'**b**'****,****'**c**'****]****[****'**a**'****,****'**c**'****,****'**c**'****]****[****'**b**'****,****'**c**'****,****'**c**'****]****[****'**c**'****,****'**c**'****,****'**c**'****]**

- snippet.juliarepl
julia> @enum FRUIT apple=1 orange=2 kiwi=3 julia> f

**(**x::FRUIT**)**=**"**I**'**m a FRUIT with value: $**(**Int**(**x**)****)****"**f**(**generic function with 1 method**)**julia> f**(**apple**)****"**I**'**m a FRUIT with value: 1**"**julia> instances**(**FRUIT**)****(**apple::FRUIT = 1**,**orange::FRUIT = 2**,**kiwi::FRUIT = 3**)**

To convert integer vectors into categoricals vectors:

- snippet.juliarepl
julia> using CategoricalArrays julia> const y= categorical

**(**rand**(****'**a**'**:**'**d**'****,**1000**)****)**; ## 1**,**000 random characters**,**from**'**a**'**to**'**d**'**julia> levels**(**y**)**## the principally useful operation for Categories 4-element Array**{**Char**,**1**}**:**'**a**'****'**b**'****'**c**'****'**d**'**

A number of features were not discussed.

- Julia has full support for rational numbers (e.g., via
`//`

). - Julia has full support for complex numbers.
- Julia has the full C complement of bitwise operators:
`~`

,`&`

,`|`

,`<<`

,`>>`

,`>>>`

,`xor`

. - Julia has full support for Booleans and for BitVectors

- Unfortunately, it is not possible to turn on a compiler warning whenever an uninitialized variable is used before assignment. (
`x= Vector{Float64}(undef,3); y= x[1]+2`

.) - The above has not (yet) discussed constructs such as
`x=Union{Int,String}`

, which would allow both`x=1`

and`x="hi"`

, but not`x=1.0`

. Such unions are especially important for dealing with missing observations. It has also not yet discussed`Any`

, which is an extremely polygamous wildcard form of Union. - See also structs for an example of how to define a limited-range numeric type.
- The NaNMath package traps some function exceptions, returning zero.

numbers.txt · Last modified: 2018/11/22 20:48 (external edit)