Spec-Zone.ru › Julia 1.11

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

Целые и числа с плавающей точкой являются основными строительными блоками арифметики и вычислений. Встроенные представления таких значений называются числовыми примитивами, а представления целых и чисел с плавающей точкой как непосредственных значений в коде известны как числовые литералы. Например, 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

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

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

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 по своей природе выполняют модулярную арифметику, отражая характеристики целочисленной арифметики на современной компьютерной аппаратной платформе. В сценариях, где возможно переполнение, крайне важно явно проверять эффекты обхода, которые могут возникнуть от таких переполнений. Модуль Base.Checked предоставляет набор арифметических операций, снабжённых проверками переполнения, которые вызывают ошибки, если переполнение происходит. Для случаев использования, где переполнение недопустимо в любых обстоятельствах, рекомендуется использовать тип 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.
  • Для еще более подробного описания истории, обоснования и проблем с числами с плавающей запятой, а также обсуждения многих других тем в области вычислительной математики, см. собранные работы Уильяма Кахана, обычно известного как «отец арифметики с плавающей запятой». Возможно, будет интересна статья An Interview with the Old Man of Floating-Point.

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

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

Существуют конструкторы для создания этих типов из примитивных числовых типов, и можно использовать строковый литерал строковый литерал @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

Связь между setprecision или setrounding и @big_str, макросом, используемым для big строковых литералов (таких как big"0.3" ), может быть не очевидной, вследствие того, что @big_str является макросом. Подробности см. в документации @big_str.

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

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

Spec-Zone.ru

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