Читать книгу «Julia. Язык программирования. Быстрый старт» онлайн полностью📖 — Вадима Никитина — MyBook.
image

Поведение при переполнении

В Julia превышение максимального представляемого значения данного типа приводит к циклическому поведению. Пример (функции typemax(), typemin(), возвращают максимальное и минимальное значения для заданного типа, == оператор равенства):

julia> x = typemax(Int64)

9223372036854775807

julia> x+1

–9223372036854775808

julia> x + 1 == typemin(Int64)

true

julia> x = typemax(UInt64)

0xffffffffffffffff

julia> x+1

0x0000000000000000

julia> x + 1 == typemin(UInt64)

true

В тех случаях, когда переполнение возможно, рекомендуется производить проверку на цикличное поведение. В противном случае используйте тип BigInt арифметики произвольной точности. Ниже приведен пример поведения при переполнении и как его можно решить с помощью BigInt():

julia> 10^19

–8446744073709551616

julia> BigInt(10)^19

10000000000000000000

Числа с плавающей точкой

Типы чисел с плавающей точкой в Julia:




Числа с плавающей точкой вводятся и выводятся стандартным образом:


julia> 1.0

1.0


julia> 1.

1.0


julia> 0.5

0.5


julia> .5

0.5


julia> -1.23

–1.23


При необходимости можно использовать E-нотацию:


julia> 1e10

1.0e10


julia> 2.5e-4

0.00025


Все результаты из примеров выше имеют тип Float64 (тип по умолчанию). Если вы хотите ввести значение с типом Float32, то необходимо использовать f вместо e следующим образом:


julia> x = 0.5f0

0.5f0


julia> typeof(x)

Float32


julia> 2.5f-4

0.00025f0


Значение с типом Float16:


julia> Float16(4.)

Float16(4.0)


julia> 2*Float16(4.)

Float16(8.0)

Ноль с плавающей точкой

Числа с плавающей точкой имеют два нуля – положительный нуль и отрицательный нуль. Они равны друг другу, но имеют разные двоичные представления, что можно увидеть с помощью функции bitstring(), которая дает буквальное битовое представление примитивного типа:


julia> 0.0 == -0.0

true


julia> bitstring(0.0)

"0000000000000000000000000000000000000000000000000000000000000000"


julia> bitstring(-0.0)

"1000000000000000000000000000000000000000000000000000000000000000"


Когда точности или размерности Float64 недостаточно, можно использовать специальный тип BigFloat:


julia> 2.0^100/4

3.1691265005705735e29


julia> BigFloat(2.0)^100/4

3.16912650057057350374175801344e+29


BigFloat знаковый тип арифметики произвольной точности, не назначаемый автоматически при вводе, а требующий явного объявления для использования.


Функции минимального и максимального значений для типов также применимы:


julia> (typemin(Float16),typemax(Float16))

(-Inf16, Inf16)


julia> (typemin(Float32),typemax(Float32))

(-Inf32, Inf32)


julia> (typemin(Float64),typemax(Float64))

(-Inf, Inf)


Результатом будут специальные значения – отрицательная и положительная бесконечности. Значения чисел превышающих числовой диапазон типа также будут заменены на специальные значения:


julia> 4.2^1000

Inf


julia> -4.2^1000

–Inf

Специальные значения

Существует три определенных стандартных значения с плавающей точкой, которые не соответствуют ни одной точке на линии вещественных чисел:






По стандарту IEEE 754, эти значения с плавающей точкой являются результатами определенных арифметических операций:


julia> 1/0

Inf


julia> -5/0

–Inf


julia> 0.000001/0

Inf


julia> 0/0

NaN


julia> 1/Inf

0.0


julia> 1/-Inf

–0.0


julia> -1/Inf

–0.0


julia> -1/-Inf

0.0


julia> 500 + Inf

Inf


julia> 500 – Inf

–Inf


julia> Inf + Inf

Inf


julia> -Inf -Inf

–Inf


julia> Inf – Inf

NaN


julia> Inf * Inf

Inf


julia> Inf*-Inf

–Inf


julia> -Inf * -Inf

Inf


julia> Inf / Inf

NaN


julia> Inf /-Inf

NaN


julia> -Inf /Inf

NaN


julia> -Inf /-Inf

NaN


julia> 0 * Inf

NaN


julia> 0 *-Inf

NaN

Тип NaN

NaN не равно, не меньше и не больше чего-либо, включая самого себя:


julia> NaN == NaN

false


julia> NaN != NaN

true


julia> NaN < NaN

false


julia> NaN > NaN

false


Это может вызвать проблемы, например при работе с массивами:


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

false


Функции Julia для работы со специальными значениями:






Функция isequal() считает NaNs равными друг другу:

julia> isequal(NaN, NaN)

true


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

true


julia> isequal(NaN, NaN32)

true


Функцию isequal() можно также использовать для различения знаковых нулей:

julia> -0.0 == 0.0

true


julia> isequal(-0.0, 0.0)

false

Машинный эпсилон

Большинство реальных чисел не могут быть точно представлены числами с плавающей точкой, поэтому для многих целей важно знать расстояние между двумя соседними представляемыми числами с плавающей точкой, которое часто называют машинным эпсилоном.


Функция eps() в Julia дает расстояние между 1.0 и следующим большим значением с плавающей точкой, при использовании в качестве аргумента типа числа с плавающей точкой:

julia> eps(Float16)

Float16(0.000977)


julia> eps(Float32)

1.1920929f-7


julia> eps(Float64)

2.220446049250313e-16


julia> eps(BigFloat)

1.727233711018888925077270372560079914223200072887256277004740694033718360632485e-77


Функция eps также может принимать в качестве аргумента значение с плавающей точкой, и выдавать абсолютную разницу между этим значением и следующим представимым значением с плавающей точкой. Другими словами, eps(x) выдает значение того же типа, что и x, такое, что x + eps(x) является следующим представимым значением с плавающей точкой, большим, чем x. Тип значения при этом также учитывается:

julia> eps(1.0)

2.220446049250313e-16


julia> eps(1000.)

1.1368683772161603e-13


julia> eps(1e-27)

1.793662034335766e-43


julia> eps(0.0)

5.0e-324


Расстояние между двумя соседними представляемыми числами с плавающей точкой не является постоянным, оно меньше для меньших значений и больше для больших значений. Другими словами, представляемые числа с плавающей запятой наиболее плотно расположены на линии вещественных чисел вблизи нуля и становятся более редкими экспоненциально по мере удаления от нуля. По определению, eps(1.0) – это то же самое, что eps(Float64), поскольку 1.0 – это 64-битное значение с плавающей точкой.


Если число не имеет точного представления с плавающей точкой, оно будет округлено до соответствующего представляемого значения. По умолчанию Julia использует режим округления, называемый RoundNearest. Он округляет до ближайшего целого числа, а ничьи округляются до ближайшего четного целого числа.

Базовые операторы Julia

Следующие арифметические операторы поддерживаются для всех примитивных числовых типов:




Несколько простых примеров с использованием арифметических операторов:


julia> 1+10-5

6


julia> 2-6

–4


julia> 5*20/10

10.0


julia> 20\10

0.5


julia> 3^3 27


julia> 5.5%-2

1.5


Число, помещенное непосредственно перед идентификатором или круглыми скобками, например, 2x или 2(x+y), рассматривается как умножение.

Логические операторы

Для типов Bool поддерживаются следующие логические операторы:






Примеры использования логических операторов:


julia> !true

false


julia> !false

true


julia> true && true

true


julia> true && false

false


julia> false && false

false


julia> true || true

true


julia> true || false

true


julia> false || false

false

Побитовые операторы

Следующие побитовые операторы поддерживаются для всех примитивных целочисленных типов:






julia> ~100

–101


julia> 121 & 232

104


julia> 121 | 232

249


julia> 121 ⊻ 232 #Знак юникода

145


julia> xor(121, 232)

145


julia> ~UInt32(121)

0xffffff86


julia> ~UInt8(121)

0x86

Операторы обновления

Каждый арифметические и побитовый оператор имеет обновляющую версию, которую можно сформировать, поставив знак равенства (=) сразу после оператора. Этот оператор обновления присваивает результат операции обратно своему левому операнду.

Версии всех двоичных арифметических и побитовых операторов:






Пример использования операторов обновления:


julia> x = 25

25


julia> x += 25

50


julia> x

50


Оператор обновления переопределяет переменную в левой части. В результате тип переменной может измениться:


julia> x = 0x01

0x01


julia> typeof(x)

UInt8


julia> x *= 2

2


julia> typeof(x)

Int64

Векторизированные “точечные” операторы

Для каждого бинарного оператора существует соответствующий "точечный" оператор, который применяет оператор поэлементно над многоэлементными структурами (массивы и т.п.).

Примеры использования “точечного” оператора:


julia> [2, 4 ,6].^2

3-element Vector{Int64}:

4

16

36


julia> x=[1 2 3 4 5 ; 6 7 8 9 10]

2×5 Matrix{Int64}:

1 2 3 4 5

6 7 8 9 10


julia> x.+1

2×5 Matrix{Int64}:

2 3 4 5 6

7 8 9 10 11


julia> x

2×5 Matrix{Int64}:

1 2 3 4 5

6 7 8 9 10


julia> x .+=1

2×5 Matrix{Int64}:

2 3 4 5 6

7 8 9 10 11


julia> x

2×5 Matrix{Int64}:

2 3 4 5 6

7 8 9 10 11


Обратите внимание, "точечные" операторы обновления изменяют исходные значения, в отличии от остальных, создающих новые.

Операторы сравнения

Для всех примитивных числовых типов определены стандартные операции сравнения:






Пример использования операторов сравнения:


julia> 2 == 2.0

true


julia> 3 == 5

false


julia> 3 != 5

true


julia> 3 < 5

true


Целочисленные числа сравниваются стандартным образом – путем сравнения битов. Числа с плавающей точкой сравниваются в соответствии со стандартом IEEE 754:

Конечные числа упорядочиваются обычным образом.

Положительный ноль равен, но не больше отрицательного нуля.

Inf равно самому себе и больше, чем все остальные, кроме NaN.

–Inf равно самому себе и меньше всех остальных, кроме NaN.

NaN не равно, не меньше и не больше ничего, включая самого себя.


Использование "точечных" операторов сравнения для многоэлементных структур возвращает булев массив с результатами сравнения для каждого элемента:


julia> [1,2,3,4,5].<= 3

5-element BitVector:

1

1

1

0

0


Сравнения в Julia можно объединять в цепочки:


julia> 10 < 15 <= 20 < 30 == 30 > 20 >= 10 == 10 < 30 != 5

true


julia> x=[1 2 3 4 5 ; 6 7 8 9 10]

2×5 Matrix{Int64}:

1 2 3 4 5

6 7 8 9 10


julia> 1 .< x .< 7

2×5 BitMatrix:

0 1 1 1 1

1 0 0 0 0

Приоритет и ассоциативность операторов

Julia применяет следующий порядок и ассоциативность операций, от высшего к низшему:






Числовые коэффициенты, например, 2x, рассматриваются как умножение с более высоким старшинством, чем любая другая двоичная операция, за исключением ^, где они имеют старшинство только как экспонента:


julia> x = 3

3


julia> 2x^2

18


julia> 2^2x

64

Базовые функции

Julia поддерживает три формы числового преобразования, которые отличаются друг от друга обработкой неточных преобразований:


Записи вида T(x) или convert(T,x) преобразуют x в значение типа T. Результат зависит от следующих двух случаев:


● Если T – тип с плавающей точкой, то результатом будет ближайшее представимое значение, которое может быть положительной или отрицательной бесконечностью.


● Если T является целочисленным типом, и x не может быть представлен в типе T (наличие значащих цифр после запятой или переполнение типа Т), то возникает ошибка InexactError.


Запись вида x%T преобразует целое число x в значение целочисленного типа T, соответствующее x по модулю 2^n. Здесь n представляет количество битов в T. Другими словами, двоичное представление усекается, чтобы оно соответствовало размеру. Применимо только для целых чисел и целочисленных типов, переполнение типа обрабатывается стандартно.


Функции округления принимают тип T в качестве необязательного аргумента. Например, round(Int,x) – это сокращение для Int(round(x)). Переполнение типа вызывает ошибку.


Пример числовых преобразований, описанных выше:


julia> Int8(100)

100


julia> Int8(100.25)

ERROR: InexactError: Int8(100.25)


julia> Int8(128)

ERROR: InexactError: trunc(Int8, 128)


julia> 100 % Int8

100


julia> 128 % Int8

–128


julia> 129 % Int8

–127


julia> round(Int8, 100.51)

101


julia> round(Int8, 127.51)

ERROR: InexactError: trunc(Int8, 128.0)