Целые и числа с плавающей точкой
Целые и числа с плавающей точкой являются основными строительными блоками арифметики и вычислений. Встроенные представления таких значений называются числовыми примитивами, а представления целых чисел и чисел с плавающей точкой как непосредственных значений в коде известны как числовые литералы. Например, 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 | |
Int128 |
✓ | 128 | -2^127 | 2^127 - 1 |
UInt128 |
128 | 0 | 2^128 - 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 julia> 0x11112222333344445555666677778888 0x11112222333344445555666677778888 julia> typeof(ans) UInt128
Это поведение основано на наблюдении, что при использовании беззнаковых шестнадцатеричных литералов для целочисленных значений, обычно используются они для представления фиксированной числовой последовательности байтов, а не просто целочисленного значения.
Вспомните, что переменная 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, когда их второй аргумент равен нулю.
Числа с плавающей точкой
Литералы чисел с плавающей точкой представляются в стандартных форматах, используя экспоненциальную запись при необходимости:
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; однако он недоступен в свободном доступе онлайн.
- Для краткого, но ясного представления о том, как представлены числа с плавающей запятой, см. статью Джона Д. Кука на эту тему, а также его введение в некоторые из проблем, возникающих из-за того, что это представление отличается в поведении от идеализированной абстракции действительных чисел.
- Также рекомендуется серия постов в блоге Брюса Доусона о числах с плавающей запятой.
- Для отличного глубокого обсуждения чисел с плавающей запятой и проблем с точностью вычислений, выполняемых с их использованием, см. статью Дэвида Голдберга Что каждый специалист по компьютерным наукам должен знать об арифметике с плавающей запятой.
- Для еще более подробной документации о истории, обосновании и проблемах с числами с плавающей запятой, а также обсуждения многих других тем в вычислительной математике, см. собранные труды Уильяма Кахана, известного как «отец арифметики с плавающей запятой». Особого интереса заслуживает интервью со старым человеком с плавающей запятой.
Арифметика произвольной точности
Для выполнения вычислений с целыми числами и числами с плавающей запятой произвольной точности 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
Приоритет коэффициентов числовых литералов немного ниже, чем у унарных операторов, таких как отрицание. Таким образом, -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–2019 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.1.1/manual/integers-and-floating-point-numbers/