Целые и числа с плавающей точкой
Целые и числа с плавающей точкой являются основными строительными блоками арифметики и вычислений. Встроенные представления таких значений называются числовыми примитивами, а представления целых и чисел с плавающей точкой как непосредственных значений в коде известны как числовые литералы. Например, 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 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; однако он не доступен в свободном доступе онлайн.
- Для краткого и ясного представления о представлении чисел с плавающей точкой см. статью Джона Д. Кука статью по этой теме, а также его введение в некоторые проблемы, возникающие из-за того, что это представление отличается по поведению от идеализированной абстракции действительных чисел.
- Также рекомендуется серия блогов Брюса Доусона о числах с плавающей точкой.
- Для отличного и глубокого обсуждения чисел с плавающей точкой и проблем численной точности при вычислениях с ними см. статью Дэвида Голдберга 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
Приоритет числовых коэффициентов литералов немного ниже, чем у унарных операторов, таких как отрицание. Таким образом, -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.3.1/manual/integers-and-floating-point-numbers/