Целые и вещественные числа
Целые и вещественные значения являются основными строительными блоками арифметики и вычислений. Встроенные представления таких значений называются числовыми примитивами, а представления целых и вещественных чисел как непосредственных значений в коде известны как числовые литералы. Например, 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 |
Кроме того, полная поддержка комплексных и рациональных чисел построена на основе этих примитивных числовых типов. Все числовые типы взаимодействуют естественным образом без явного приведения типов, благодаря гибкой, расширяемой пользователем системе повышения типов.
Целые числа
... (rest of the document) ...Напомним, что переменная 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 в разделе Арифметика с произвольной точностью.
Пример поведения при переполнении и способы его потенциального решения приведены ниже:
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> 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 для целых и чисел с плавающей запятой произвольной точности соответственно.
Существуют конструкторы для создания этих типов из примитивных числовых типов, и литерал строки @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.5.3/manual/integers-and-floating-point-numbers/