Целые и числа с плавающей точкой
Целые и числа с плавающей точкой являются основными строительными блоками арифметики и вычислений. Встроенные представления таких значений называются числовыми примитивами, а представления целых и чисел с плавающей точкой в виде непосредственных значений в коде называются числовыми литералами. Например, 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.) 8.0f0
Подчеркивание _ может использоваться в качестве разделителя цифр:
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; однако он не доступен для бесплатного онлайн-доступа.
- Для краткого и ясного представления о том, как представлены числа с плавающей запятой, см. статью Джона Д. Кука на эту тему, а также его введение в некоторые проблемы, возникающие из-за того, что это представление отличается по поведению от идеализированной абстракции вещественных чисел.
- Также рекомендуется серия блогов Брюса Доусона о числах с плавающей запятой.
- Для превосходного, глубокого обсуждения чисел с плавающей запятой и проблем с точностью вычислений с их использованием см. статью Дэвида Голдберга Что каждый программист должен знать об арифметике с плавающей запятой.
- Для ещё более подробной информации об истории, обосновании и проблемах с числами с плавающей запятой, а также для обсуждения многих других тем по численным вычислениям, см. собранные работы Уильяма Кахана, обычно известного как «Отец арифметики с плавающей запятой». Особого внимания заслуживает интервью со Стариком арифметики с плавающей запятой.
Арифметика с произвольной точностью
Для выполнения вычислений с целыми числами и числами с плавающей запятой произвольной точности 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
Кроме того, выражения в скобках могут использоваться как коэффициенты для переменных, подразумевая умножение выражения на переменную:
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.5/manual/integers-and-floating-point-numbers/