Spec-Zone.ru › Julia 0.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
UInt32 32 0 232 - 1
Int64 ✓ 64 -263 263 - 1
UInt64 64 0 264 - 1
Int128 ✓ 128 -2127 2127 - 1
UInt128 128 0 2128 - 1
Bool N/A 8 false (0) true (1)
  • Типы чисел с плавающей точкой:

Тип Точность Количество битов
Float16 полуточная 16
Float32 одинарная 32
Float64 двойная 64

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

Целые числа

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

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> 0x1
0x01

julia> typeof(ans)
UInt8

julia> 0x123
0x0123

julia> typeof(ans)
UInt16

julia> 0x1234567
0x01234567

julia> typeof(ans)
UInt32

julia> 0x123456789abcdef
0x0123456789abcdef

julia> typeof(ans)
UInt64

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

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

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

julia> 0b10
0x02

julia> typeof(ans)
UInt8

julia> 0o10
0x08

julia> typeof(ans)
UInt8

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

Поведение при переполнении

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

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

Литералы чисел с плавающей точкой представлены в стандартных форматах:

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:

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)

Ноль с плавающей точкой

Числа с плавающей точкой имеют два нуля, положительный ноль и отрицательный ноль. Они равны друг другу, но имеют разные двоичные представления, как можно увидеть, используя функцию bits:

julia> 0.0 == -0.0
true

julia> bits(0.0)
"0000000000000000000000000000000000000000000000000000000000000000"

julia> bits(-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> bits(prevfloat(x))
"00111111100111111111111111111111"

julia> bits(x)
"00111111101000000000000000000000"

julia> bits(nextfloat(x))
"00111111101000000000000000000001"

Этот пример иллюстрирует общий принцип, что смежные представимые числа с плавающей точкой также имеют смежные двоичные целочисленные представления.

Режимы округления

Если число не имеет точного представления с плавающей точкой, оно должно быть округлено до соответствующего представимого значения, но если необходимо, способ этого округления может быть изменен в соответствии с режимами округления, представленными в стандарте IEEE 754.

julia> x = 1.1; y = 0.1;

julia> x + y
1.2000000000000002

julia> setrounding(Float64,RoundDown) do
           x + y
       end
1.2

Используемый по умолчанию режим всегда RoundNearest, который округляет до ближайшего представимого значения, а при равенстве округляет к ближайшему значению с чётным наименее значимым битом.

Предупреждение

Округление обычно верно только для основных арифметических функций (+(), -(), *(), /() и sqrt()) и операций преобразования типов. Многие другие функции предполагают, что установлен режим по умолчанию 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 для целых чисел и чисел с плавающей точкой произвольной точности соответственно.

Существуют конструкторы для создания этих типов из примитивных числовых типов, и parse() может быть использован для их построения из AbstractString.

После создания они участвуют в арифметике со всеми другими числовыми типами благодаря механизму повышения и преобразования типов Julia:

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

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

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

Приоритет коэффициентов числовых литералов такой же, как и у унарных операторов, таких как отрицание. Таким образом, 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.

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

  • Выражения, начинающиеся с 0x всегда являются шестнадцатеричными литералами.

  • Выражения, начинающиеся с числового литерала, за которым следуют e или E всегда являются литералами чисел с плавающей точкой.

Литералы нуля и единицы

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.000000000000000000000000000000000000000000000000000000000000000000000000000000

© 2009–2016 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/release-0.6/manual/integers-and-floating-point-numbers/

Spec-Zone.ru

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