Spec-Zone.ru › Julia 1.4

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

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

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

Целые числа

... (rest of the document) ... END_OF_DOCUMENT_MARKER

Напомним, что переменная ans устанавливается в значение последнего выражения, вычисленного в интерактивной сессии. Это не происходит при выполнении кода Julia другими способами.

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

julia> 0b10
0x02

julia> typeof(ans)
UInt8

julia> 0o010
0x08

julia> typeof(ans)
UInt8

julia> 0x00000000000000001111222233334444
0x00000000000000001111222233334444

julia> typeof(ans)
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 в Арифметике с произвольной точностью.

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

Целочисленное деление (функция 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> 0.5f0
0.5f0

julia> typeof(ans)
Float32

julia> 2.5f-4
0.00025f0

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

julia> Float32(-1.5)
-1.5f0

julia> typeof(ans)
Float32

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

julia> 0x1p0
1.0

julia> 0x1.8p3
12.0

julia> 0x.4p-1
0.125

julia> typeof(ans)
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

Функции 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. После создания они участвуют в арифметических операциях со всеми другими числовыми типами благодаря механизму преобразования и повышения типа Julia:

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

julia> big"123456789012345678901234567890" + 1
123456789012345678901234567891

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

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.
  • Выражение с плавающей точкой 1e10 может быть интерпретировано как числовой литерал 1, умноженный на переменную e10, и аналогично с эквивалентной формой E.
  • Выражение с 32-битной плавающей точкой 1.5f22 может быть интерпретировано как числовой литерал 1.5, умноженный на переменную f22.

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

  • Выражения, начинающиеся с 0x всегда являются шестнадцатеричными литералами.
  • Выражения, начинающиеся с числового литерала, за которым следуют 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–2020 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.4.2/manual/integers-and-floating-point-numbers/

Spec-Zone.ru

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