Spec-Zone.ru › Julia 1.7

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

Целые и числа с плавающей точкой являются основными строительными блоками арифметики и вычислений. Встроенные представления таких значений называются числовыми примитивами, а представления целых и чисел с плавающей точкой как непосредственных значений в коде называются числовыми литералами. Например, 1 — это целочисленный литерал, а 1.0 — литерал числа с плавающей точкой; их двоичные представления в памяти в виде объектов являются числовыми примитивами.

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

Ниже приведены примитивные числовые типы Julia:

  • Целочисленные типы:
Тип Целый? Количество битов Наименьшее значение Наибольшее значение
Int8 ✓ 8 -27 27 - 1
UInt8 8 0 28 - 1
Int16 ✓ 16 -215 215 - 1
UInt16 16 0 216 - 1
Int32 ✓ 32 -231 231 - 1
UInt32 32 0 232 - 1
  • Типы чисел с плавающей точкой:

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

Целые числа

Литералы целых чисел представлены стандартным образом:

julia> 1
1

julia> 1234
1234

По умолчанию тип целочисленного литерала зависит от того, имеет ли целевая система 32-битную или 64-битную архитектуру:

# 32-bit system:
julia> typeof(1)
Int32

# 64-bit system:
julia> typeof(1)
Int64

Внутренняя переменная Julia Sys.WORD_SIZE указывает, является ли целевая система 32-битной или 64-битной:

# 32-bit system:
julia> Sys.WORD_SIZE
32

# 64-bit system:
julia> Sys.WORD_SIZE
64

Julia также определяет типы Int и UInt, которые являются псевдонимами соответственно для знакового и беззнакового нативного целочисленного типа системы:

# 32-bit system:
julia> Int
Int32
julia> UInt
UInt32

# 64-bit system:
julia> Int
Int64
julia> UInt
UInt64

Более крупные целочисленные литералы, которые не могут быть представлены только 32 битами, но могут быть представлены в 64 битах, всегда создают 64-битные целые числа, независимо от типа системы:

# 32-bit or 64-bit system:
julia> typeof(3000000000)
Int64

Беззнаковые целые числа вводятся и выводятся с использованием префикса 0x и шестнадцатеричных (основание 16) цифр 0-9a-f (заглавные цифры A-F также работают для ввода). Размер беззнакового значения определяется количеством используемых шестнадцатеричных цифр:

julia> x = 0x1
0x01

julia> typeof(x)
UInt8

julia> x = 0x123
0x0123

julia> typeof(x)
UInt16

julia> x = 0x1234567
0x01234567

julia> typeof(x)
UInt32

julia> x = 0x123456789abcdef
0x0123456789abcdef

julia> typeof(x)
UInt64

julia> x = 0x11112222333344445555666677778888
0x11112222333344445555666677778888

julia> typeof(x)
UInt128

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

END_OF_DOCUMENT_MARKER

Также поддерживаются двоичные и восьмеричные литералы:

julia> x = 0b10
0x02

julia> typeof(x)
UInt8

julia> x = 0o010
0x08

julia> typeof(x)
UInt8

julia> x = 0x00000000000000001111222233334444
0x00000000000000001111222233334444

julia> typeof(x)
UInt128

Что касается шестнадцатеричных литералов, двоичные и восьмеричные литералы генерируют беззнаковые целые типы. Размер двоичного элемента данных — минимально необходимый размер, если старшая цифра литерала не 0. В случае ведущих нулей размер определяется минимально необходимым размером для литерала, имеющего ту же длину, но с ведущей цифрой 1. Это позволяет пользователю контролировать размер. Значения, которые не могут быть сохранены в UInt128 , не могут быть записаны как такие литералы.

Двоичные, восьмеричные и шестнадцатеричные литералы могут быть со знаком - непосредственно перед беззнаковым литералом. Они генерируют беззнаковое целое число того же размера, что и беззнаковый литерал, со значением, обратным по модулю два:

julia> -0x2
0xfe

julia> -0x0002
0xfffe

Минимальные и максимальные представимые значения примитивных числовых типов, таких как целые числа, задаются функциями typemin и typemax:

julia> (typemin(Int32), typemax(Int32))
(-2147483648, 2147483647)

julia> for T in [Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128]
           println("$(lpad(T,7)): [$(typemin(T)),$(typemax(T))]")
       end
   Int8: [-128,127]
  Int16: [-32768,32767]
  Int32: [-2147483648,2147483647]
  Int64: [-9223372036854775808,9223372036854775807]
 Int128: [-170141183460469231731687303715884105728,170141183460469231731687303715884105727]
  UInt8: [0,255]
 UInt16: [0,65535]
 UInt32: [0,4294967295]
 UInt64: [0,18446744073709551615]
UInt128: [0,340282366920938463463374607431768211455]

Значения, возвращаемые функциями typemin и typemax, всегда имеют тип заданного аргумента. (Вышеприведенное выражение использует несколько функций, которые еще не были представлены, включая циклы for, строки и интерполяцию, но должно быть достаточно понятным для пользователей с некоторым опытом программирования.)

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

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

julia> x = typemax(Int64)
9223372036854775807

julia> x + 1
-9223372036854775808

julia> x + 1 == typemin(Int64)
true

Таким образом, арифметика с целыми числами Julia фактически представляет собой форму модулярной арифметики. Это отражает характеристики базовой арифметики целых чисел, реализованной на современных компьютерах. В приложениях, где переполнение возможно, важно явно проверять циклическое повторение, вызванное переполнением; в противном случае рекомендуется использовать тип BigInt в разделе Арифметика с произвольной точностью.

Пример поведения при переполнении и способа его потенциального решения приведен ниже:

julia> 10^19
-8446744073709551616

julia> big(10)^19
10000000000000000000

Ошибки при делении

Целочисленное деление (функция div) имеет два исключительных случая: деление на ноль и деление наименьшего отрицательного числа (typemin) на -1. В обоих случаях генерируется исключение DivideError. Функции остатка и модуля (rem и mod) генерируют исключение DivideError, когда их второй аргумент равен нулю.

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

Литералы чисел с плавающей точкой представлены в стандартных форматах, используя обозначение E при необходимости:

julia> 1.0
1.0

julia> 1.
1.0

julia> 0.5
0.5

julia> .5
0.5

julia> -1.23
-1.23

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

Значения могут быть легко преобразованы в тип Float32:

julia> x = Float32(-1.5)
-1.5f0

julia> typeof(x)
Float32

Шестнадцатеричные числа с плавающей запятой также допустимы, но только как значения Float64, с p перед экспонентой в двоичной системе:

julia> 0x1p0
1.0

julia> 0x1.8p3
12.0

julia> x = 0x.4p-1
0.125

julia> typeof(x)
Float64

Числа с плавающей запятой полуточной точности также поддерживаются (Float16), но они реализованы в программном обеспечении и используют Float32 для вычислений.

julia> sizeof(Float16(4.))
2

julia> 2*Float16(4.)
Float16(8.0)

Подчёркивание _ может использоваться в качестве разделителя цифр:

julia> 10_000, 0.000_000_005, 0xdead_beef, 0b1011_0010
(10000, 5.0e-9, 0xdeadbeef, 0xb2)

Ноль с плавающей запятой

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

julia> 0.0 == -0.0
true

julia> bitstring(0.0)
"0000000000000000000000000000000000000000000000000000000000000000"

julia> bitstring(-0.0)
"1000000000000000000000000000000000000000000000000000000000000000"

Особые значения с плавающей запятой

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

Float16 Float32 Float64 Название Описание
Inf16 Inf32 Inf положительная бесконечность значение, большее всех конечных значений с плавающей запятой
-Inf16 -Inf32 -Inf отрицательная бесконечность значение, меньшее всех конечных значений с плавающей запятой
NaN16 NaN32 NaN не число значение, не == ни одному значению с плавающей запятой (включая само себя)

Для дальнейшего обсуждения того, как эти бесконечные значения с плавающей запятой упорядочиваются относительно друг друга и других чисел с плавающей запятой, см. Числовые сравнения. Согласно стандарту IEEE 754, эти значения с плавающей запятой являются результатом определённых арифметических операций:

julia> 1/Inf
0.0

julia> 1/0
Inf

julia> -5/0
-Inf

julia> 0.000001/0
Inf

julia> 0/0
NaN

julia> 500 + Inf
Inf

julia> 500 - Inf
-Inf

julia> Inf + Inf
Inf

julia> Inf - Inf
NaN

julia> Inf * Inf
Inf

julia> Inf / Inf
NaN

julia> 0 * Inf
NaN

julia> NaN == NaN
false

julia> NaN != NaN
true

julia> NaN < NaN
false

julia> NaN > NaN
false

Функции typemin и typemax также применяются к типам с плавающей запятой:

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

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

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

Машинная эпсилон

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

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

julia> eps(Float32)
1.1920929f-7

julia> eps(Float64)
2.220446049250313e-16

julia> eps() # same as eps(Float64)
2.220446049250313e-16

Эти значения равны 2.0^-23 и 2.0^-52 как значениям Float32 и Float64 соответственно. Функция 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 также предоставляет функции nextfloat и prevfloat, которые возвращают следующее большее или меньшее представимое число с плавающей запятой к аргументу соответственно:

julia> x = 1.25f0
1.25f0

julia> nextfloat(x)
1.2500001f0

julia> prevfloat(x)
1.2499999f0

julia> bitstring(prevfloat(x))
"00111111100111111111111111111111"

julia> bitstring(x)
"00111111101000000000000000000000"

julia> bitstring(nextfloat(x))
"00111111101000000000000000000001"

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

Режимы округления

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

Используемый по умолчанию режим всегда RoundNearest, который округляет до ближайшего представимого значения, причём в случае совпадения округляется к ближайшему значению с чётным наименее значимым битом.

Описание и ссылки

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

  • Определяющим руководством по арифметике чисел с плавающей запятой является стандарт IEEE 754-2008; однако он не доступен бесплатно онлайн.
  • Для краткого, но ясного представления о том, как представлены числа с плавающей запятой, см. статью Джона Д. Кука на эту тему, а также его введение в некоторые проблемы, возникающие из-за того, как это представление отличается по поведению от идеализированной абстракции действительных чисел.
  • Также рекомендуется серия статей Брюса Доусона в блоге о числах с плавающей запятой.
  • Для превосходного глубокого обсуждения чисел с плавающей запятой и проблем численной точности при работе с ними см. статью Дэвида Голдберга Что каждый компьютерный ученый должен знать о плавающей арифметике.
  • Для ещё более обширной документации по истории, обоснованию и проблемам чисел с плавающей запятой, а также обсуждения многих других тем в вычислительной математике, см. собранные труды Уильяма Кахана, обычно известного как «отец чисел с плавающей запятой». Особого внимания может заслуживать интервью со Стариком чисел с плавающей запятой.

Арифметика с произвольной точностью

Для возможности вычислений с целыми числами и числами с плавающей запятой произвольной точности Julia использует библиотеку GNU Multiple Precision Arithmetic Library (GMP) и GNU MPFR Library соответственно. Типы BigInt и BigFloat доступны в Julia для целых чисел и чисел с плавающей запятой произвольной точности соответственно.

Конструкторы существуют для создания этих типов из примитивных числовых типов, и литералы строк строковый литерал @big_str или parse могут использоваться для их построения из AbstractString. BigInt также могут быть введены как целочисленные литералы, когда они слишком велики для других встроенных целых типов. Обратите внимание, что поскольку нет беззнакового целого типа произвольной точности в Base (BigInt в большинстве случаев достаточно), можно использовать шестнадцатеричные, восьмеричные и двоичные литералы (в дополнение к десятичным).

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

julia> BigInt(typemax(Int64)) + 1
9223372036854775808

julia> big"123456789012345678901234567890" + 1
123456789012345678901234567891

julia> parse(BigInt, "123456789012345678901234567890") + 1
123456789012345678901234567891

julia> string(big"2"^200, base=16)
"100000000000000000000000000000000000000000000000000"

julia> 0x100000000000000000000000000000000-1 == typemax(UInt128)
true

julia> 0x000000000000000000000000000000000
0

julia> typeof(ans)
BigInt

julia> big"1.23456789012345678901"
1.234567890123456789010000000000000000000000000000000000000000000000000000000004

julia> parse(BigFloat, "1.23456789012345678901")
1.234567890123456789010000000000000000000000000000000000000000000000000000000004

julia> BigFloat(2.0^66) / 3
2.459565876494606882133333333333333333333333333333333333333333333333333333333344e+19

julia> factorial(BigInt(40))
815915283247897734345611269596115894272000000000

Однако повышение типа между примитивными типами выше и BigInt/BigFloat не является автоматическим и должно быть явно указано.

julia> x = typemin(Int64)
-9223372036854775808

julia> x = x - 1
9223372036854775807

julia> typeof(x)
Int64

julia> y = BigInt(typemin(Int64))
-9223372036854775808

julia> y = y - 1
-9223372036854775809

julia> typeof(y)
BigInt

Точность по умолчанию (в количестве битов мантиссы) и режим округления операций с BigFloat могут быть изменены глобально, вызвав setprecision и setrounding, и все последующие вычисления учтут эти изменения. В качестве альтернативы точность или режим округления могут быть изменены только в пределах выполнения определённого блока кода, используя те же функции с блоком do:

julia> setrounding(BigFloat, RoundUp) do
           BigFloat(1) + parse(BigFloat, "0.1")
       end
1.100000000000000000000000000000000000000000000000000000000000000000000000000003

julia> setrounding(BigFloat, RoundDown) do
           BigFloat(1) + parse(BigFloat, "0.1")
       end
1.099999999999999999999999999999999999999999999999999999999999999999999999999986

julia> setprecision(40) do
           BigFloat(1) + parse(BigFloat, "0.1")
       end
1.1000000000004

Коэффициенты числовых литералов

Для большей наглядности общих числовых формул и выражений Julia позволяет переменным предшествовать числовому литералу, подразумевая умножение. Это делает запись полиномиальных выражений гораздо чище:

julia> x = 3
3

julia> 2x^2 - 3x + 1
10

julia> 1.5x^2 - .5x + 1
13.0

Это также делает запись экспоненциальных функций более элегантной:

julia> 2^2x
64

Приоритет числовых литеральных коэффициентов несколько ниже, чем у унарных операторов, таких как отрицание. Таким образом, -2x разбирается как (-2) * x и √2x разбирается как (√2) * x. Однако числовые литеральные коэффициенты разбираются аналогично унарным операторам при объединении с возведением в степень. Например, 2^3x разбирается как 2^(3x), а 2x^3 разбирается как 2*(x^3).

Числовые литералы также работают как коэффициенты для выражений в скобках:

julia> 2(x-1)^2 - 3(x-1) + 1
3

Приоритет числовых литеральных коэффициентов, используемых для неявного умножения, выше, чем у других бинарных операторов, таких как умножение (*), и деление (/, \, и //). Это означает, например, что 1 / 2im равно -0.5im и 6 // 2(2 + 1) равно 1 // 1.

Кроме того, выражения в скобках могут использоваться как коэффициенты для переменных, подразумевая умножение выражения на переменную:

julia> (x-1)x
6

Однако ни конкатенация двух выражений в скобках, ни размещение переменной перед выражением в скобках не могут использоваться для подразумеваемого умножения:

julia> (x-1)(x+1)
ERROR: MethodError: objects of type Int64 are not callable

julia> x(x+1)
ERROR: MethodError: objects of type Int64 are not callable

Оба выражения интерпретируются как применение функции: любое выражение, которое не является числовым литералом, за которым сразу следует скобочное выражение, интерпретируется как функция, применённая к значениям в скобках (подробнее о функциях см. Функции). Таким образом, в обоих случаях возникает ошибка, так как левое значение не является функцией.

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

Синтаксические конфликты

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

  • Шестнадцатеричное целое выражение 0xff может интерпретироваться как числовой литерал 0, умноженный на переменную xff. Аналогичные неоднозначности возникают с восьмеричными и двоичными литералами, такими как 0o777 или 0b01001010.
  • Выражение с плавающей точкой 1e10 может интерпретироваться как числовой литерал 1, умноженный на переменную e10, и аналогично с эквивалентной формой E.
  • Выражение 32-битного числа с плавающей точкой 1.5f22 может интерпретироваться как числовой литерал 1.5, умноженный на переменную f22.

Во всех случаях неоднозначность разрешается в пользу интерпретации как числовых литералов:

  • Выражения, начинающиеся с 0x/0o/0b — всегда шестнадцатеричные/восьмеричные/двоичные литералы.
  • Выражения, начинающиеся с числового литерала, за которым следуют e или E — всегда литералы с плавающей точкой.
  • Выражения, начинающиеся с числового литерала, за которым следует f — всегда литералы 32-битного числа с плавающей точкой.

В отличие от E, которое по историческим причинам эквивалентно e в числовых литералах, F — просто другая буква и не ведет себя как f в числовых литералах. Следовательно, выражения, начинающиеся с числового литерала, за которым следует F интерпретируются как числовой литерал, умноженный на переменную, что означает, например, что 1.5F22 равно 1.5 * F22.

Литералы ноль и один

Julia предоставляет функции, которые возвращают литералы 0 и 1, соответствующие указанному типу или типу заданной переменной.

Функция Описание
zero(x) Литерал ноль типа x или типа переменной x
one(x) Литерал единица типа x или типа переменной x

Эти функции полезны в Числовых сравнениях для избежания накладных расходов от ненужного преобразования типов.

Примеры:

julia> zero(Float32)
0.0f0

julia> zero(1.0)
0.0

julia> one(Int32)
1

julia> one(BigFloat)
1.0

© 2009–2021 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.7.0/manual/integers-and-floating-point-numbers/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API