산술 연산과 기본 함수

Julia가 제공하는 숫자형 타입은 산술 연산자와 비트 연산자, 다양한 수학적 함수를 지원한다.

산술 연산자

다음 산술 연산자들은 모든 숫자형 타입에 사용할 수 있다:

표현식이름설명
+x단항 덧셈항등 연산
-x단항 뺄셈덧셈의 역원 반환
x + y덧셈일반적인 덧셈
x - y곱셈일반적인 뺄셈
x * y곱셈일반적인 곱셈
x / y나눗셈일반적인 나눗셈
x ÷ y정수 나눗셈(몫)x / y의 몫 반환
x \ y역 나눗셈y / x와 동일
x ^ y제곱xy제곱을 반환
x % y나머지rem(x,y)와 동일(나머지를 반환)

Bool 타입에 대한 부정 연산도 가능하다:

표현식이름설명
!x부정 연산truefalse로 바꾸거나 혹은 그 반대

줄리아의 타입 치환 시스템은 산술 연산이 자연스럽게 작동하게 한다. 자세한 것은 Conversion and Promotion을 참고하라.

산술 연산자를 활용한 간단한 예제다:

julia> 1 + 2 + 3
6

julia> 1 - 2
-1

julia> 3*2/12
0.5

(일반적으로 근처 다른 연산자보다 먼저 적용되는 경우 간격을 밀접하게 두는 경우가 있다. 예를 들어 x를 음수로 먼저 변환하고 2를 반환하는 코드는 편의상 -x + 2로 쓴다)

비트 연산자

비트 연산자는 모든 기본 정수형 타입을 지원한다:

ExpressionName
~x비트 부정
x & y비트 and 연산
x | y비트 or 연산
x ⊻ y비트 xor 연산 (exclusive or)
x >>> ylogical shift right
x >> yarithmetic shift right
x << ylogical/arithmetic shift left

비트 연산자를 활용한 간단한 예제다:

julia> ~123
-124

julia> 123 & 234
106

julia> 123 | 234
251

julia> 123 ⊻ 234
145

julia> xor(123, 234)
145

julia> ~UInt32(123)
0xffffff84

julia> ~UInt8(123)
0x84

업데이트 연산자

산술 연산자와 비트 연산자는 그에 대응하는 업데이트 연산자가 있습니다. 업데이트 연산자는 변수의 값과 새롭게 제시된 피연산자로 계산한 후 결과를 다시 해당 변수에 저장합니다. 업데이트 연산자는 기존 연산자 기호 우측에 =를 붙임으로써 만들 수 있습니다. 예를 들어 x += 3x = x + 3와 같은 의미가 됩니다:

julia> x = 1
1

julia> x += 3
4

julia> x
4

각 산술/비트 연산자에 대응하는 업데이트 연산자는 아래와 같습니다:

+=  -=  *=  /=  \=  ÷=  %=  ^=  &=  |=  ⊻=  >>>=  >>=  <<=
Note

Julia는 상황에 따라 타입을 바꾸기 때문에, 업데이트 연산자가 변수의 타입을 바꿀 수 있다.

julia> x = 0x01; typeof(x)
UInt8

julia> x *= 2 # Same as x = x * 2
2

julia> typeof(x)
Int64

배열에서의 연산("dot" 연산자)

^와 같은 모든 이진 연산자는 배열의 원소별 연산을 위한 "dot" 연산자 .^가 있다. 따라서 [1,2,3]의 모든 원소를 세제곱 하고 싶다면 [1,2,3] ^ 3이 아니라 [1,2,3] .^ 3로 작성해야 한다. !같은 단항 연산자도 사용할 수 있다(.!).

julia> [1,2,3] .^ 3
3-element Array{Int64,1}:
  1
  8
 27

julia> .![true,false,true]
3-element BitArray{1}:
 0
 1
 0

보다 구체적으로, a .^ b(^).(a,b)로 해석되고, 여기서 .broadcast 연산을 한다: broadcast 연산은 배열과 스칼라, 배열과 배열(모양이 달라도 됨)을 원소별 연산이 가능하게 같은 모양의 배열로 "적절히" 바꿔준다(예를 들어 row 벡터와 column 백터가 들어오면 행렬을 생성한다). 또한 "dot" 연산자는 근처 다른 "dot" 연산자와 결합하여 반복문을 한번만 돌리도록 설계되었다. 만약 2 .* A.^2 .+ sin.(A)(혹은 @. macro)을 사용하여 @. 2A^2 + sin(A)를 계산한다면, Julia는 A의 모든 원소에 대해 2a^2 + sin(a)를 계산한다. f.(g.(x))같은 nested dot 호출도 이런 최적화가 일어나기 때문에 x .+ 3 .* x.^2(+).(x, (*).(3, (^).(x, 2)))같은 함수 꼴로 사용해도 성능상 차이가 발생하지 않는다.

나아가서, in-place 융합된 대입 연산 .=에 대해 a .+= b (or @. a += b)와 같은 "dot" 업데이트 연산자들은 a .= a .+ b로 구문분석된다(are parsed).

(dot 문법 문서을 참고하라).

dot 연산자는 사용자 정의 연산자에서도 활용할 수 있다. 예를 들어 ⊗(A,B) = kron(A,B)를 정의했다면 [A,B] .⊗ [C,D][A⊗C, B⊗D]를 계산한다.

dot 연산자를 숫자형 리터럴과 혼용하는 것은 해석의 모호성을 야기할 수 있다. 예를 들어 1.+x1. + x인지 1 .+ x인지 확실하지 않다. 따라서 이런 문법은 지원하지 않으며, 불가피하게 사용할 시 여백으로 문법을 명확히 해야한다.

비교 연산

모든 기본 숫자형 타입은 비교연산을 지원한다(복소수 예외):

연산자설명
==상등
!=, 상등 부정
<작다
<=, 작거나 같다
>크다
>=, 크거나 같다

아래 예제로 사용법을 볼 수 있다:

julia> 1 == 1
true

julia> 1 == 2
false

julia> 1 != 2
true

julia> 1 == 1.0
true

julia> 1 < 2
true

julia> 1.0 > 3
false

julia> 1 >= 1.0
true

julia> -1 <= 1
true

julia> -1 <= -1
true

julia> -1 <= -2
false

julia> 3 < -0.5
false

정수에서 비교연산은 같은 위치의 비트를 비교하는 방식으로 이뤄진다. 반면 실수는 IEEE 754 standard의 규칙에 따라 비교한다:

  • 유한한 수는 일반적인 방식으로 이뤄진다.
  • +0과 -0은 서로 같다.
  • InfNaN와 자신을 제외한 수보다 크고, 자기 자신과는 같다.
  • InfNaN와 자신을 제외한 수보다 작고, 자기 자신과는 같다.
  • NaN 는 자신을 포함한 그 어떤 수와 같지 않고, 크지도 않고, 작지도 않다.

마지막 규칙은 다른 규칙보다 극단적이라, 실제 계산에서 예상치 못한 결과를 야기할 수 있다:

julia> NaN == NaN
false

julia> NaN != NaN
true

julia> NaN < NaN
false

julia> NaN > NaN
false

이러한 문제는 특히 배열을 다룰 때 골머리를 썩게 할 것이다:

julia> [1 NaN] == [1 NaN]
false

Julia는 해시값처럼 특수한 값에도 비교연산을 사용할 수 있도록 함수를 지원한다:

함수반환값이 참인 조건
isequal(x, y)xy 가 같은 때
isfinite(x)x가 유한한 수일 대
isinf(x)x가 무한한 수일 때
isnan(x)x가 숫자가 아닐 때

isequal에서 NaN이 서로 같다고 나온다:

julia> isequal(NaN, NaN)
true

julia> isequal([1 NaN], [1 NaN])
true

julia> isequal(NaN, NaN32)
true

isequal은 +0과 -0을 구분할 때도 사용할 수 있다:

julia> -0.0 == 0.0
true

julia> isequal(-0.0, 0.0)
false

정수의 signed나 unsigned 혹은 실수 사이의 비교연산은 까다롭다. Julia는 타입 충돌 없이 이런 것들이 잘 작동하게 보장한다.

서로 다른 타입에서 isequal을 사용하면 ==을 호출하게 되어있다. 당신이 자신만의 타입에서 동일성을 정의하고 싶다면 == method를 정의하면 된다. 여기에 hash method도 정의하면 isequal(x,y)hash(x) == hash(y)을 반환한다.

비교연산 이어쓰기

대부분의 언어가 지원하지 않지만, Python의 비교연산 문법처럼 비교연산을 이어쓸 수 있다:

julia> 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
true

비교연산 이어쓰기는 코드 구성을 깔끔하게 한다. 비교연산 이어쓰기는 &&를 사용하여 연산을 한 것과 똑같이 작용하고, 원소별 연산에서는 &을 사용한 것과 동일하다. 쉽게 말하면 우리가 수학적으로 예상한 것과 똑같이 나온다는 것이다. 그 예로 0 .< A .< 1는 각 원소가 0과 1 사이에 있는지에 대한 참/거짓을 행렬로 반환한다.

Note the evaluation behavior of chained comparisons:

julia> v(x) = (println(x); x)
v (generic function with 1 method)

julia> v(1) < v(2) <= v(3)
2
1
3
true

julia> v(1) > v(2) <= v(3)
2
1
false

첫번째 결과에서 중간값이 한번만 계산됨을 확인할 수 있다. 이를 통해 v(1) < v(2) && v(2) <= v(3)로 계산했을 때보다 적은 계산량을 가지고, 비교연산 이어쓰기에서는 기존 프로그래밍 언어와 달리 계산 순서는 미리 예측할 수 없다는 걸 확인할 수 있다. 따라서 비교연산 이어쓰기에서는 계산 순서가 중요한 연산(예시: 입출력)을 하지 말자. 이런 부작용을 감안하고 써야한다면 && 연산자를 활용하자. (Short-Circuit Evaluation을 참고하라).

기본 함수

Julia는 수치 계산을 위한 함수와 연산자를 전폭적으로 지원한다. 이런 연산은 서로 다른 타입의 숫자(정수, 실수, 유리수 등)가 충돌 없이 수학적인 결과와 맞아 떨어지게끔 되어있다.

이런 함수 모두 dot 문법을 지원한다. 예를 들어 sin.(A)는 array의 모든 A의 원소의 sin값을 구한다.

연산자 우선순위와 결합성

아래 표는 높은 우선 순위부터 낮은 우선순위별로 연산자를 나열하고, 연산자 결합성을 확인할 수 있다:

분류연산자결합성
문법. followed by ::왼쪽
제곱^오른쪽
단항 연산+ - √오른쪽[1]
Bitshifts<< >> >>>왼쪽
분수//왼쪽
곱셈, 나눗셈* / % & \ ÷왼쪽[2]
덧셈, 뺄셈+ - | ⊻왼쪽[2]
문법: ..왼쪽
문법|>왼쪽
문법<|오른쪽
비교 연산> < >= <= == === != !== <:결합성 없음
제어 흐름&& followed by || followed by ?오른쪽
Pair=>오른쪽
할당= += -= *= /= //= \= ^= ÷= %= |= &= ⊻= <<= >>= >>>=오른쪽

모든 Julia 연산자의 우선순위 목록을 보고 싶다면, 다음 파일의 최상단 코드를 참고하라: src/julia-parser.scm

Base.operator_precedence을 통해서도 우선순위를 확인할 수 있다. 반환값이 높을수록 더 우선순위가 더 높다:

julia> Base.operator_precedence(:+), Base.operator_precedence(:*), Base.operator_precedence(:.)
(11, 13, 17)

julia> Base.operator_precedence(:sin), Base.operator_precedence(:+=), Base.operator_precedence(:(=))  # (Note the necessary parens on `:(=)`)
(0, 1, 1)

연산자 결합성 확인은 Base.operator_associativity로 확인할 수 있다:

julia> Base.operator_associativity(:-), Base.operator_associativity(:+), Base.operator_associativity(:^)
(:left, :none, :right)

julia> Base.operator_associativity(:⊗), Base.operator_associativity(:sin), Base.operator_associativity(:→)
(:left, :none, :right)

:sin의 경우 우선순위가 0임을 확인할 수 있는데, 0은 최하위 우선순위가 아니라 유효하지 않은 연산자를 나타낸다. 이와 비슷한 이유로 이런 연산자는 연산자 결합성이 :none임을 볼 수 있다.

Numerical Conversions

Julia supports three forms of numerical conversion, which differ in their handling of inexact conversions.

  • 표기법 T(x) 또는 convert(T,x)x를 type T의 값으로 변환한다.

    • 만약 T가 부동 소숫점 type이면, 결과값은 표현할 수 있는 가장 가까운 값으로 나타내며, 이는 양 혹은 음의 무한대도 될 수 있다.
    • 만약 T가 정수 type이면, xT type으로 나타낼 수 없을 때, InexactError가 발생한다.
  • x % T는 정수 x를 법 2^n에 대해 합동(congruent to x modulo 2^n)인, type T의 정수값으로 변환한다(converts an integer x to a value of integer type T). 여기서 nT 안의 비트 수이다. In other words, the binary representation is truncated to fit.

  • The Rounding functions take a type T as an optional argument. For example, round(Int,x) is a shorthand for Int(round(x)).

The following examples show the different forms.

julia> Int8(127)
127

julia> Int8(128)
ERROR: InexactError: trunc(Int8, 128)
Stacktrace:
[...]

julia> Int8(127.0)
127

julia> Int8(3.14)
ERROR: InexactError: Int8(3.14)
Stacktrace:
[...]

julia> Int8(128.0)
ERROR: InexactError: Int8(128.0)
Stacktrace:
[...]

julia> 127 % Int8
127

julia> 128 % Int8
-128

julia> round(Int8,127.4)
127

julia> round(Int8,127.6)
ERROR: InexactError: trunc(Int8, 128.0)
Stacktrace:
[...]

See Conversion and Promotion for how to define your own conversions and promotions.

Rounding 함수

함수설명반환값
round(x)round x to the nearest integertypeof(x)
round(T, x)round x to the nearest integerT
floor(x)round x towards -Inftypeof(x)
floor(T, x)round x towards -InfT
ceil(x)round x towards +Inftypeof(x)
ceil(T, x)round x towards +InfT
trunc(x)round x towards zerotypeof(x)
trunc(T, x)round x towards zeroT

나눗셈 함수

함수설명
div(x,y), x÷ytruncated division; quotient rounded towards zero
fld(x,y)floored division; quotient rounded towards -Inf
cld(x,y)ceiling division; quotient rounded towards +Inf
rem(x,y)remainder; satisfies x == div(x,y)*y + rem(x,y); sign matches x
mod(x,y)modulus; satisfies x == fld(x,y)*y + mod(x,y); sign matches y
mod1(x,y)mod with offset 1; returns r∈(0,y] for y>0 or r∈[y,0) for y<0, where mod(r, y) == mod(x, y)
mod2pi(x)modulus with respect to 2pi; 0 <= mod2pi(x)   < 2pi
divrem(x,y)returns (div(x,y),rem(x,y))
fldmod(x,y)returns (fld(x,y),mod(x,y))
gcd(x,y...)greatest positive common divisor of x, y,...
lcm(x,y...)least positive common multiple of x, y,...

부호 함수와 절댓값 함수

함수설명
abs(x)x의 절댓값
abs2(x)x절댓값의 제곱
sign(x)x의 부호. -1, 0, 혹은 +1를 반환
signbit(x)sign bit가 1인지(true) 혹은 0인지(false)인지 반환
copysign(x,y)a value with the magnitude of x and the sign of y
flipsign(x,y)a value with the magnitude of x and the sign of x*y

지수, 로그, 루트 함수

함수설명
sqrt(x), √xsquare root of x
cbrt(x), ∛xcube root of x
hypot(x,y)hypotenuse of right-angled triangle with other sides of length x and y
exp(x)natural exponential function at x
expm1(x)accurate exp(x)-1 for x near zero
ldexp(x,n)x*2^n computed efficiently for integer values of n
log(x)natural logarithm of x
log(b,x)base b logarithm of x
log2(x)base 2 logarithm of x
log10(x)base 10 logarithm of x
log1p(x)accurate log(1+x) for x near zero
exponent(x)binary exponent of x
significand(x)binary significand (a.k.a. mantissa) of a floating-point number x

For an overview of why functions like hypot, expm1, and log1p are necessary and useful, see John D. Cook's excellent pair of blog posts on the subject: expm1, log1p, erfc, and hypot.

삼각 함수와 쌍곡선 함수

Julia는 모든 삼각 함수와 쌍곡선 함수를 지원한다:

sin    cos    tan    cot    sec    csc
sinh   cosh   tanh   coth   sech   csch
asin   acos   atan   acot   asec   acsc
asinh  acosh  atanh  acoth  asech  acsch
sinc   cosc

이 함수들은 인자를 하나만 받지만, 예외적으로 atan는 2개를 받을 수 있으며 이는 atan2에 대응한다.

추가로 sinpi(x)cospi(x)sin(pi*x), cos(pi*x)와 결과는 비슷지만 더 정확한 결과를 산출할 수 있다.

삼각 함수 단위에 호도법(radian)대신 도(°)를 사용하려면 접미사 d를 붙인다. 예를 들어 sind(x)x°의 sin값을 구한다. 아래는 접미사 d를 사용한 모든 삼각 함수를 나열했다:

sind   cosd   tand   cotd   secd   cscd
asind  acosd  atand  acotd  asecd  acscd

특수 함수

이외에도 다양한 수치 계산용 함수를 패키지로 받을 수 있다 SpecialFunctions.jl.

  • 1단항연산자 +-를 연속해서 사용하는 경우, 업데이트 연산자(++)와 구별하기 위해 괄호를 명시적으로 사용해야 한다. 다른 단항 연산자와 같이 사용하는 경우엔 right-associativity 규칙에 따라 구문을 분석한다(예시: √√-a√(√(-a))로 분석).
  • 2The operators +, ++ and * are non-associative. a + b + c is parsed as +(a, b, c) not +(+(a, b), c). However, the fallback methods for +(a, b, c, d...) and *(a, b, c, d...) both default to left-associative evaluation.