Spec-Zone.ru › Julia 1.6

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

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

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

Целые числа

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

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, когда их второй аргумент равен нулю.

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

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

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

Функции 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 всегда являются литералами с плавающей точкой.
  • Выражения, начинающиеся с числового литерала, за которым следует e всегда являются литералами 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.6.0/manual/integers-and-floating-point-numbers/

Spec-Zone.ru

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