Spec-Zone.ru › Julia 1.9

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

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

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

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

  • Целочисленные типы:
Тип С знаком? Количество битов Наименьшее значение Наибольшее значение
Int8 ✓ 8 -2^7 2^7 - 1
UInt8 8 0 2^8 - 1
Int16 ✓ 16 -2^15 2^15 - 1
UInt16 16 0 2^16 - 1
Int32 ✓ 32 -2^31 2^31 - 1
UInt32 32 0 2^32 - 1
Int64 ✓ 64 -2^63 2^63 - 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
... (rest of the content) ...

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

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

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. Это означает, что:

  • 0x1 и 0x12 являются UInt8 литералами,
  • 0x123 и 0x1234 являются UInt16 литералами,
  • 0x12345 и 0x12345678 являются UInt32 литералами,
  • 0x123456789 и 0x1234567890adcdef являются UInt64 литералами и т. д.

Даже если есть ведущие нули, которые не влияют на значение, они учитываются для определения размера хранения литерала. Таким образом, 0x01 является UInt8, а 0x0001 является UInt16.

Это позволяет пользователю управлять размером.

Беззнаковые литералы (начинающиеся с 0x) которые кодируют целые числа, слишком большие, чтобы быть представленными как UInt128 значения, вместо этого будут строить BigInt значения. Это не беззнаковый тип, но это единственный встроенный тип, достаточно большой, чтобы представить такие большие целочисленные значения.

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

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 при нулевом втором аргументе.

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

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

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; однако он не доступен для бесплатного онлайн просмотра.
  • Для краткого, но ясного представления о том, как представлены числа с плавающей точкой, см. статью Джона Д. Кука на эту тему, а также его введение в некоторые проблемы, возникающие из-за различий в поведении этого представления от идеализированной абстракции вещественных чисел.
  • Рекомендуется также серия блоговых записей Брюса Доусона о числах с плавающей точкой.
  • Для отличного подробного обсуждения чисел с плавающей точкой и проблем с точностью вычислений при вычислениях с ними см. статью Дэвида Голдберга What Every Computer Scientist Should Know About Floating-Point Arithmetic.
  • Для еще более подробной информации об истории, обосновании и проблемах с числами с плавающей точкой, а также обсуждения многих других тем в вычислительной математике, см. собранные работы Уильяма Кахана, известного как «отец чисел с плавающей точкой». Особо интересной может быть беседа с «стариком чисел с плавающей точкой».

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

Для возможности вычислений с целыми числами и числами с плавающей точкой произвольной точности 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–2023 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.9/manual/integers-and-floating-point-numbers/

Spec-Zone.ru

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