Spec-Zone.ru › Julia 1.8

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

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

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

Целые числа

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

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 литералом.

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

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

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

Для возможности вычислений с целыми числами и числами с плавающей точкой произвольной точности 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

Числовые коэффициенты литералов

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

Spec-Zone.ru

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