Engee documentation

Mathematical operations and elementary functions

Julia provides a complete set of basic arithmetic and bit operators for all numeric primitive types of the language, as well as an efficient and portable implementation of a comprehensive set of standard mathematical functions.

Arithmetic operators

The following are supported for all primitive numeric types https://en.wikipedia.org/wiki/Arithmetic#Arithmetic_operations [arithmetic operators].

Expression Name Description

+x

The unary plus

The identity operation

-x

Unary minus sign

Displays values in their additive inversions

x + y

Binary Plus

Performs addition

x - y

Binary negative

Performs subtraction

x * y

Multiplication

Performs multiplication

x / y

Division

Performs division

x ÷ y

Integer division

x /y rounded to an integer

x \ y

Division with inversion

Equivalent to y / x

x ^ y

Exponentiation

Raises x to the power of y

x % y

Remains

Equivalent to rem(x, y)

Putting a numeric literal directly in front of an identifier or parentheses, such as 2x or 2(x + y), is considered a multiplication, and with a higher priority than other binary operations. For more information: see Numeric literal coefficients.

Thanks to the Julia promotion system, arithmetic operations on combinations of different types of arguments are performed automatically, naturally, and intuitively. For more information about this system, see Transformation and promotion.

You can use \div<tab> in the REPL or the Julia IDE to conveniently enter the sign. For more information: see section of the guide on entering Unicode characters.

Here are some simple examples of using arithmetic operators.

julia> 1 + 2 + 3
6

julia> 1 - 2
-1

julia> 3*2/12
0.5

(By convention, operators executed earlier than other nearby operators are separated by a shorter distance. For example, writing -x + 2 usually means that x is accessed first, and then 2 is added to the result.)

When used in multiplication, the value `false' acts as a _ strong zero_.

julia> NaN * false
0.0

julia> false * Inf
0.0

This prevents the substitution of NaN values in obviously zero amounts. The rationale: https://arxiv.org/abs/math/9205211 [Knut (1992)].

Logical operators

For types Bool the following are supported https://en.wikipedia.org/wiki/Boolean_algebra#Operations [logical operators].

Expression Name

!x

Denial

x && y

Calculated using the abbreviated "and" scheme

x || y

Calculated using the abbreviated "or" scheme

Negation changes true to `false', and vice versa. Explanations for the calculation operations according to the abbreviated scheme are available at the links provided.

Note that Bool is an integer type and all the standard promotion rules and numeric operators are defined for it as well.

Bitwise operators

For all primitive integer types, the following are supported https://en.wikipedia.org/wiki/Bitwise_operation#Bitwise_operators [bitwise operators].

Expression Name

~x

Bitwise "not"

x & y

Bitwise "and"

x | y

Bitwise "or"

x ⊻ y

The bit-exclusive "or"

x ⊼ y

Bitwise "and not"

x ⊽ y

Bitwise "or not"

x >>> y

Logical shift to the right

x >> y

Arithmetic shift to the right

x << y

Logical/Arithmetic left shift

Here are some examples of using bitwise operators.

julia> ~123
-124

julia> 123 & 234
106

julia> 123 | 234
251

julia> 123 ⊻ 234
145

julia> xor(123, 234)
145

julia> nand(123, 123)
-124

julia> 123 ⊼ 123
-124

julia> nor(123, 124)
-128

julia> 123 ⊽ 124
-128

julia> ~UInt32(123)
0xffffff84

julia> ~UInt8(123)
0x84

Assignment operators

All binary arithmetic and bitwise operators also have an assignment version that passes the result to the operand on the left. To use the assignment version of the binary operator, you must specify the = sign immediately after the operator. For example, the entry x += 3 is equivalent to `x = x + 3'.

julia> x = 1
1

julia> x += 3
4

julia> x
4

All versions of binary arithmetic and bit assignment operators:

+=  -=  *=  /=  \=  ÷=  %=  ^=  &=  |=  ⊻=  >>>=  >>=  <<=

The assignment operator overrides the value of the variable on the left side. As a result, it is possible to change the type of the variable.

julia> x = 0x01; typeof(x)
UInt8
julia> x *= 2 # Is similar to x = x * 2 2

julia> typeof(x) Int64

Vector operators with a dot

For every binary operation like ^, there is a corresponding "dot" operation .^ that is automatically defined to perform ^ element-by-element on arrays. For example, [1, 2, 3] ^ 3 is not defined, since there is no standard mathematical meaning to "cubing" a (non-square) array, but [1, 2, 3] .^ 3 is defined as computing the elementwise (or "vectorized") result [1^3, 2^3, 3^3]. Similarly for unary operators like ! or , there is a corresponding .√ that applies the operator elementwise.

julia> [1, 2, 3] .^ 3
3-element Vector{Int64}:
  1
  8
 27

For example, a .^ b is analyzed as dot call (^).(a,b), which performs the operation broadcasts. This operation allows you to combine arrays with scalar values, combine arrays of the same size with each other (performed element by element), and even arrays of different shapes (for example, horizontal and vertical vectors, creating a matrix). In addition, like all "dot" calls, dot operators are connecting. For example, when calculating the expression +2 .* A.^2 .+ sin for the array `A.(A)` (or the equivalent expression `@. 2A^2 + sin(A)` with a macro xref:base/arrays.adoc#Base.Broadcast.@__dot__[`@.`]) a single loop is executed with the array `A`, in which `+2a^2 + sin(a)` is calculated for each element a from A. In particular, nested dot calls such as f.(g.(x)) are combined, and adjacent binary operators such as x .+3 .*x.^2 are equivalent to nested dot calls (+).(x, (*).(3, (^).( x, 2))).

In addition, dot operators with assignment, such as a .+= b (or @. a += b), are analyzed as a .= a .+ b, where .= represents the combined operation of assigning _ to a place (see documentation on syntax with a dot).

The dot syntax is also applicable to user-defined operators. For example, if you define ⊗(A, B) = kron(A, B) to use the convenient infix syntax A ⊗ B for the Kronecker product (kron), then [A, B] .⊗ [C, D] will calculate [A⊗C, B⊗D] without the need to write additional code.

When dot operators are combined with numeric literals, ambiguity may occur. For example, what does 1.+x mean? It could be 1. + x or 1 .+ x. Therefore, this syntax is not allowed; in such cases, you need to put spaces around the operator.

Comparing numbers

Standard comparison operations are defined for all primitive numeric types.

Operator Name

==

Equality

!=,

Inequality

<

Less

<=,

Less than or equal to

>

More

>=,

Greater than or equal to

Here are some simple examples.

julia> 1 == 1
true

julia> 1 == 2
false

julia> 1 != 2
true

julia> 1 == 1.0
true

julia> 1 < 2
true

julia> 1.0 > 3
false

julia> 1 >= 1.0
true

julia> -1 <= 1
true

julia> -1 <= -1
true

julia> -1 <= -2
false

julia> 3 < -0.5
false

Integers are compared using the standard bit comparison. Floating point numbers are compared according to https://en.wikipedia.org/wiki/IEEE_754-2008 [IEEE 754 standard]:

  • the final numbers are ordered in the usual way;

  • a positive zero is equal to a negative one, but not greater than it;

  • the value of Inf is equal to itself and greater than all other values except NaN;

  • the value of -Inf is equal to itself and smaller than all other values except NaN;

  • the value of NaN is not equal to any values, not more or less than any values, including this value itself.

The last point may be unexpected, so it makes sense to note the following.

julia> NaN == NaN
false

julia> NaN != NaN
true

julia> NaN < NaN
false

julia> NaN > NaN
false

It can also create problems when working with arrays.

julia> [1 NaN] == [1 NaN]
false

Additional functions are available in Julia to check special values of numbers, which can be useful, for example, when comparing a hash key.

Function What checks

isequal(x, y)

The 'x` and y are identical.

isfinite(x)

'x` is a finite number.

isinf(x)

'x' is infinite.

isnan(x)

'x` is not a number.

isequal evaluates the values of NaN as equals to each other:

julia> isequal(NaN, NaN)
true

julia> isequal([1 NaN], [1 NaN])
true

julia> isequal(NaN, NaN32)
true

'isequal` also allows you to distinguish between signed zeros.

julia> -0.0 == 0.0
true

julia> isequal(-0.0, 0.0)
false

Comparing different types of values when comparing signed, unsigned, and floating-point integers is often difficult. Significant efforts have been made to ensure that they are executed correctly in Julia.

For other types, isequal calls by default ==, so if you want to define equality for your own type, just add a method for it. ==. If you are defining your own equality function, it makes sense to define an appropriate method. hash so that isequal(x,y) implies `hash(x) == hash(y)'.

A chain of comparisons

Unlike most languages, https://en.wikipedia.org/wiki/Python_syntax_and_semantics#Comparison_operators [with the notable exception of Python], arbitrary comparison chains are possible in Julia.

julia> 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
true

Comparison chains are often convenient to use in numeric code. They use the && operator to compare scalar values and the operator & for element-by-element comparisons, which allows arrays to be used in chains. For example, 0 .< A .< 1 outputs an array of boolean values, the elements of which are true when the corresponding elements of A are in the range from 0 to 1.

Pay attention to the order in which the comparison chains work.

julia> v(x) = (println(x); x)
v (generic function with 1 method)

julia> v(1) < v(2) <= v(3)
2
1
3
true

julia> v(1) > v(2) <= v(3)
2
1
false

The expression in the middle is evaluated only once, not twice, as it would be when writing an expression in the form v(1) < v(2) && v(2) <= v(3). However, the order of calculations in the comparison chain is uncertain. It is strongly discouraged to use expressions with side effects (such as printing) in them. If these side effects are necessary, you should explicitly use the '&&` operator (see Calculations according to an abbreviated scheme).

Elementary functions

A comprehensive set of mathematical functions and operators is available in Julia. These mathematical operations are defined for the widest possible class of numeric values for which they make sense, including integers, floating-point numbers, rational and complex numbers.

In addition, these functions (like any Julia functions) can be used as vector functions for arrays and other collections using syntax with a dot f.(A). For example, sin.(A) calculates the sine values for each element in the array `A'.

Operator precedence and associativity

The Julia language uses the following order and associativity of operators, from the highest priority to the lowest.

Category Operators Associativity

Syntax

. and then ::

Lefthand

Exponentiation

^

The right one

Unary

+ - ! ~ ¬ √ ∛ ∜ ⋆ ± ∓ <: >:

Right Footnote:1[The unary operators + and - require explicit parentheses around the argument to distinguish these operators from ++ and others. Other combinations of unary operators are analyzed with associativity on the right, for example, √√-a is considered as √(√(-a)).]

Bit shift

<< >> >>>

Lefthand

Fractions

//

Lefthand

Multiplication

* / % & \ ÷

Left Footnote:2[The operators +, ++ and * are not associative. The expression a + b + c is analyzed as +(a, b, c), and not as +(+(a, b), c). However, the backup methods are for both +(a, b, c, d...) and *(a, b, c, d...) are calculated with left-hand associativity by default.]

Addition

+ - | ⊻

Left Footnote:2[]

Syntax

: ..

Lefthand

Syntax

|>

Lefthand

Syntax

<|

The right one

Comparison

> < >= <= == === != !== <:

Without associativity

The order of execution

'&&`, then || and then ?

The right one

Couple

=>

The right one

Assignment

= += -= *= /= //= \= ^= ÷= %= |= &= ⊻= <<= >>= >>>=

The right one

For a complete list of priorities for each Julia operator, see at the top of the file: https://github.com/JuliaLang/julia/blob/master/src/julia-parser.scm [src/julia-parser.scm]. Note that some of these operators are not defined in the Base module, but may have definitions in standard libraries, packages, or user code.

You can also find out the numerical priority value of any operator using the built-in function Base.operator_precedence (the higher the number, the higher the priority).

julia> Base.operator_precedence(:+), Base.operator_precedence(:*), Base.operator_precedence(:.)
(11, 12, 17)

julia> Base.operator_precedence(:sin), Base.operator_precedence(:+=), Base.operator_precedence(:(=))  # (Скобки в `:(=)` обязательны.)
(0, 1, 1)

A symbolic representation of operator associativity can also be obtained by calling the built-in function `Base.operator_associativity'.

julia> Base.operator_associativity(:-), Base.operator_associativity(:+), Base.operator_associativity(:^)
(:left, :none, :right)

julia> Base.operator_associativity(:⊗), Base.operator_associativity(:sin), Base.operator_associativity(:→)
(:left, :none, :right)

Please note that characters such as :sin return the priority of `0'. This value represents invalid operators, not operators with the lowest priority. Similarly, such operators are assigned the associativity `:none'.

Numeric literal coefficients, such as 2x', are treated as multiplication performed before any other binary operations. The exception is the exponentiation operation (^`), in which multiplication will be performed first only if it is in the exponent.

julia> x = 3; 2x^2
18

julia> x = 3; 2^2x
64

Their combination will be analyzed as a unary operator with standard asymmetry during exponentiation: the expressions -x^y and 2x^y are analyzed as -(x^y) and 2(x^y) respectively, whereas x^-y and x^2y are analyzed as x^(-y) and `x^(2y)'.

Number Type Conversion

Julia supports three number type conversion options, which differ in how they handle inaccurate conversions.

  • Writing T(x) or convert(T, x) converts x to a value of type `T'.

    • If T is a floating-point type, the result will be the nearest value it represents, which can be a positive or negative infinity.

    • If the type T is an integer, and the value x cannot be represented by the type T, the error InexactError is returned.

  • 'x%T` converts an integer x into an integer type value T, comparable to the result of integer division of x by 2^n, where n is the number of bits in T. In other words, the binary representation is truncated to match the type.

  • Rounding functions take the type T as an optional argument. For example, round(Int,x) is a short form of Int(round(x))'..

The various forms of conversion are shown in the following examples.

julia> Int8(127)
127

julia> Int8(128)
ERROR: InexactError: trunc(Int8, 128)
Stacktrace:
[...]

julia> Int8(127.0)
127

julia> Int8(3.14)
ERROR: InexactError: Int8(3.14)
Stacktrace:
[...]

julia> Int8(128.0)
ERROR: InexactError: Int8(128.0)
Stacktrace:
[...]

julia> 127 % Int8
127

julia> 128 % Int8
-128

julia> round(Int8,127.4)
127

julia> round(Int8,127.6)
ERROR: InexactError: Int8(128.0)
Stacktrace:
[...]

In the section Transformation and Promotion describes how you can define your own transformations and promotions.

Rounding functions

Function Description The type of the returned value

round(x)

Rounds x to the nearest integer

typeof(x)

round(T, x)

Rounds x to the nearest integer

T

floor(x)

Rounds the x in the -Inf direction

typeof(x)

floor(T, x)

Rounds the x in the -Inf direction

T

ceil(x)

Rounds x in the direction of +Inf

typeof(x)

ceil(T, x)

Rounds x in the direction of +Inf

T

trunc(x)

Rounds the x towards zero

typeof(x)

trunc(T, x)

Rounds the x towards zero

T

Division functions

Function Description

div(x, y), x÷y

Division with truncation; the quotient is rounded "to zero"

fld(x, y)

Division rounded down; quotient rounded towards -Inf

cld(x, y)

Up-rounded division; the quotient is rounded towards +Inf

rem(x, y), x%y

Remainder; satisfies the condition x == div(x, y)*y + rem(x, y); the sign corresponds to x

mod(x, y)

Remainder of integer division; satisfies the condition x == fld(x, y)*y + mod(x, y); the sign corresponds to y

mod1(x, y)

mod with offset 1; returns r∈(0, y] for y>0 or r∈[y, 0) for y<0, where mod(r, y) == mod(x, y)

mod2pi(x)

Remainder of integer division relative to 2pi; 0 <= mod2pi(x) < 2pi

divrem(x, y)

Returns (div(x, y),rem(x, y))

fldmod(x, y)

Returns (fld(x, y), mod(x, y))

gcd(x, y...)

The positive greatest common divisor is x, y, …​

lcm(x, y...)

The positive smallest common multiple of x', `y, …​

Sign and absolute value functions

Function Description

abs(x)

A positive value modulo x

abs2(x)

The module `x' squared

sign(x)

Shows the 'x` sign, returning --1, 0 or +1

signbit(x)

Indicates whether the sign bit is enabled (true) or disabled (false)

copysign(x, y)

A value modulo x', with the sign `y

flipsign(x, y)

A value modulo x', with the sign `x*y

Degrees, logarithms, and roots

Function Description

sqrt(x), √x

The square root of x

cbrt(x), ∛x

The cubic root of x

hypot(x, y)

Hypotenuse of a right triangle with legs of length x and y

exp(x)

Exponent function with exponent x

expm1(x)

Exact calculation of exp(x) -1 for x close to zero

ldexp(x, n)

Efficient calculation of x*2^n for integer values of n

log(x)

The natural logarithm of `x'.

log(b, x)

The logarithm of x based on b

log2(x)

The logarithm of 'x` in base 2

log10(x)

The logarithm of 'x` in base 10

log1p(x)

Exact calculation of log(1 + x) for x close to zero

exponent(x)

Binary exponentiation for x

significand(x)

The binary significant part (mantissa) of a floating-point number x

The necessity and usefulness of functions such as hypot, expm1 and log1p, described in two excellent articles on John D. Cook’s blog: https://www.johndcook.com/blog/2010/06/07/math-library-functions-that-seem-unnecessary /[expm1, log1p, erfc] and https://www.johndcook.com/blog/2010/06/02/whats-so-hard-about-finding-a-hypotenuse /[hypot].

Trigonometric and hyperbolic functions

All standard trigonometric and hyperbolic functions are also defined.

sin    cos    tan    cot    sec    csc
sinh   cosh   tanh   coth   sech   csch
asin   acos   atan   acot   asec   acsc
asinh  acosh  atanh  acoth  asech  acsch
sinc   cosc

They are all functions of a single argument, and atan can also take two arguments, corresponding to the traditional function https://en.wikipedia.org/wiki/Atan2 [atan2].

In addition, functions are available sinpi(x) and cospi(x) for a more accurate calculation, respectively sin(pi * x) and cos(pi * x).

To calculate trigonometric functions using degrees (rather than radians), add the ending d to the function name. For example, sind(x) calculates the sine of x, where the value of x is given in degrees. A complete list of options for trigonometric functions for degrees:

sind   cosd   tand   cotd   secd   cscd
asind  acosd  atand  acotd  asecd  acscd

Special functions

Package https://github.com/JuliaMath/SpecialFunctions.jl [SpecialFunctions.jl] contains many additional special mathematical functions.