수리수리연수리 코드얍

4. Fortran의 산술연산과 format이 없는 입출력문(이론편) 본문

즐거운 Fortran

4. Fortran의 산술연산과 format이 없는 입출력문(이론편)

ydduri 2023. 2. 11. 23:14

예시 코드: ydduri 깃허브 - Joyful_Fortran_Tutorial

목차

1. 산술연산

2. format이 없는 입출력문

1. 산술연산

포트란의 산술연산은 so easy하다. 다른 언어와도 크게 다르지 않고, 우리의 상식과도 일치하는 직관적인 문법을 가지고 있다. 쉬운 내용이지만 'FORmula TRANslator(수식 변환기)'라는 이름값을 위해 산술연산에 대해 다뤄보도록 하겠다.

1) 연산 우선순위

  1. ** (거듭제곱)
  2. * (곱하기), / (나누기)
  3. + (더하기), - (빼기)
  • 연산 우선순위가 같은 연산자들은 왼쪽부터 계산
  • 연산 순서를 바꾸려면 괄호 ()를 사용

우리의 상식과 완전히 일치하는 규칙을 볼 수 있다.

2) 실수 연산과 정수 연산

  • 실수 연산: 연산에 사용되는 숫자가 실수형일 때 결과값도 실수로 출력
  • 정수 연산: 연산에 사용되는 숫자가 모두 정수형일 때 결과값도 정수로 출력(소수점 이하 절삭)
  • 혼합 연산: 하나의 수식에 실수와 정수가 섞여 있다면 결과값을 실수로 출력

여기서 특히 주의해야 할 것은 정수 연산과 혼합 연산이다. 연산에 사용되는 숫자가 모두 정수형이라면 Fortran은 '정수 연산'을 수행한다. 예를 들어 5/2 라는 수식을 계산한다면 정확한 답은 2.5이지만, 연산에 사용되는 숫자 5와 2가 모두 정수이기 때문에 Fortran은 결과값을 정수로 출력하게 된다. 이럴 때는 소수점 이하가 절삭되어 2가 출력된다. 정확한 결과를 얻고 싶다면 5./2. 이런 식으로 Fortran이 실수로 인식하도록 만들어주어야 한다.

실수와 정수가 섞여 있는 수식의 경우 혼합 연산을 수행하여 결과값이 실수로 출력된다. 그런데 아래와 같이 정수 연산과 혼합 연산을 섞어서 쓰게 될 경우, 순서에 따라 연산 방식이 달라져 의도치 않은 결과가 출력될 수도 있다.

  • 5/2*3.0 = 6.0000 -> 5/2의 경우 정수 연산이라 2가 되고, 여기 3.0을 곱하면 혼합 연산이라 결과값을 실수로 출력하므로 6.0000이 나옴.
  • 3.0*5/2 = 7.5000 -> 3.0*5의 경우 혼합 연산이라 15.0000이 되고, 2로 나눌 때도 연산에 실수가 포함되어 있으므로 혼합 연산이라 결과값이 실수로 출력되어 7.5000이 나옴.

이밖에도 경험상 실수와 정수를 섞어 쓰면 에러가 뜬 적도 있었기 때문에, Fortran에서 숫자는 무조건 실수로 표기하는 것을 추천하는 편이다. 선언할 때도 숫자는 모두 real(실수형)로 하고, 5., 3. 이런 식으로 .을 붙여서 실수로 표기하도록 하자!

3) 내장함수

Fortran에서는 유용한 내장함수를 많이 제공한다. 대표적인 내장함수를 표로 정리하면 다음과 같다(여기서 인수란 함수에 집어넣는 숫자, 즉 괄호 안에 쓰는 값을 말한다).

함수 설명 인수 자료형 계산결과 자료형
abs(x) x의 절댓값 I, R, DP 인수와 동일
char(x) x(정수)를 문자형으로 변환 I char
cos(x) x radian의 코사인 값 R, DP 인수와 동일
dble(x) x를 DP형으로 변환 I, R DP
exp(x) 지수함수 R, DP 인수와 동일
ichar(x) 하나의 문자를 정수로 변환 char I
int(x) x를 정수형으로 변환 R, DP I
log10(x) x의 상용로그 R, DP 인수와 동일
log(x) x의 자연로그 R, DP 인수와 동일
max(x1, x2, ...) x1, x2, ...의 최댓값 I, R, DP 인수와 동일
min(x1, x2, ...) x1, x2, ...의 최솟값 I, R, DP 인수와 동일
mod(x, y) x/y 정수 연산 시의 나머지 I, R, DP 인수와 동일
nint(x) x를 반올림해 정수형으로 변환 R, DP I
real(x) x를 실수형으로 변환 I, DP R
sin(x) x radian의 사인 값 R, DP 인수와 동일
sqrt(x) x의 제곱근 R, DP 인수와 동일
tan(x) x radian의 탄젠트 값 R, DP 인수와 동일
trim(x) x 문자열 뒤의 공백 제거 char 인수와 동일

(1) 변수 종류 변환 함수(Type conversion)

  • int(x): 정수형으로 변환
  • real(x): 실수형으로 변환
  • dble(x): double precision형으로 변환
  • ichar(x): 하나의 문자를 정수로 변환
  • char(x): 정수를 문자로 변환

이때 ichar와 char의 경우 아스키 코드를 이용해 문자와 정수를 변환하는, 다소 복잡한 방식으로 작동하는 함수이기 때문에 사실 잘 사용하지 않는다. 이처럼 문자<->정수 간 변환이 필요할 때는 주로 write문을 사용하는 편인데, 이에 대해서는 나중에 다룰 예정이다.

(2) 삼각함수(4_example_1.f90)

  • sin(x), cos(x), tan(x)
  • 각각의 역삼각함수 asin(x), acos(x), atan(x)

삼각함수 내장함수를 사용할 때의 주의점은 인수를 라디안으로 입력해야 한다는 점이다. 결과값 또한 라디안으로 계산된다. Python의 경우 numpy같은 패키지에서 np.deg2rad(degree를 radian으로 변환), np.rad2deg(radian을 degree로 변환)와 같은 내장함수를 제공하지만 속도를 위해 친절함을 포기한 Fortran에는 그런 거 없다. 그래서 아래 예시처럼 수식을 활용해 degree<->radian 변환을 가능하도록 해주는 변수를 따로 선언하여 사용하는 편이다.

결과

3.14159274       1.00000000       60.0000038
  • x: sin90º를 출력하기 위해 d2rad 변수를 이용해 90º를 radian 단위로 바꿔줌. 결과는 1.00000000 radian
  • y: arccos 0.5 radian을 계산한 뒤, r2deg 변수를 이용해 결과값을 degree 단위로 바꿔줌. 결과는 60.0000038º

(3) 반올림, 올림, 내림

  • nint(x): x를 반올림해 정수형으로 변환. 5.1을 넣으면 5를 출력, 5.9를 넣으면 6을 출력
  • ceiling(x): x를 올림해 정수형으로 변환. 5.1을 넣으면 6을 출력, 5.9를 넣어도 6을 출력
  • floor(x): x를 내림해 정수형으로 변환. 5.1을 넣으면 5를 출력, 5.9를 넣어도 5를 출력

(4) 제곱근 함수 sqrt

x의 제곱근을 구하는 두 가지 방법은 아래와 같다. 물론 제곱근의 정의를 이용하는 방법도 괜찮긴 하나 수식이 길어지면 복잡해보일 수 있기 때문에 제곱근 함수를 이용하는 편을 추천한다.

  • 제곱근의 정의 이용하기: x**0.5
  • 제곱근 함수 이용하기: sqrt(x)

(5) 나머지 함수 mod

나머지 함수는 이런 문제에 활용될 수 있다. '1267시간 = ?일 ?시간으로 나타내면 어떻게 될까?'

  • 날짜 구하기: int(1267, 24) -> 1267을 24로 나눈 몫을 구하겠다는 의미(엄밀하게는 1267을 24로 나눈 결과를 정수형으로 변환하겠다, 즉 정수 연산의 결과를 구하겠다는 것이므로 결국 몫을 구한다는 것과 같음)
  • 시간 구하기: mod(1267, 24) -> 1267을 24로 나눈 나머지를 구하겠다는 의미

2. format이 없는 입출력문

Fortran에서는 입출력문을 작성할 때 주로 'format(포맷)'을 함께 지정하는 편이다. 포맷이란 입출력의 형태를 지정하는 방식으로, 자세한 것은 다음 글에서 다룰 예정이다. 여기서는 포맷이 없는 기본 입출력문을 어떻게 쓰는지에 대해서 다루겠다. 3. Fortran의 변수 체계(실전편)을 꼼꼼히 보신 분이라면 아마 출력문에 'print*'를 사용한 것을 기억하실지도 모르겠다. 이때 * 표시는 포맷을 지정하지 않겠다는 의미이다.

1) format이 없는 출력문

여기서 배우는 print는 결과를 터미널에 출력하고, 후에 배울 write는 결과를 파일 등 다른 매체에 저장할 수 있도록 해준다.

  • print*, x1, x2, ... , xm
  • 이때 x1, x2, ... , xm은 출력될 변수명 혹은 상수값을 의미한다.

아래 코드의 경우 score1, score2, score3 세 개의 시험 점수의 평균을 mean이라는 변수에 저장한 뒤 출력하도록 하였다. average score: 라는 문자열과 계산된 mean 값이 함께 출력되어 다음과 같은 결과가 나온다.(4_example_2.f90)

결과

average score:   78.3333359

2) format이 없는 입력문(4_example_3.f90)

Python에서는 input() 함수를 이용해 사용자로부터 값을 입력받도록 할 수 있는데, Fortran에서는 이를 read문으로 구현한다. 위의 코드에서는 score1, score2, score3 세 개의 점수를 코드 안에서 결정했지만, read문을 사용하면 코드를 실행할 때마다 새로운 값을 입력할 수 있다. format이 없는 입력문의 경우에도 출력문과 마찬가지로 *을 붙여서 read*로 나타낸다.

  • read*, x1, x2, ... , xm
  • 이때 x1, x2, ... , xm은 값을 입력 받을 변수명을 의미한다.

이런 식으로 read문이 포함된 코드를 실행시키면 터미널에 아무것도 안 뜨는데, 이건 실행이 안 되고 있는 게 아니라 터미널을 통해 변수가 입력될 때까지 기다리고 있는 것이다. 우리는 score1, score2, score3 세 개 점수를 입력해야 하는데, 해당 변수들을 '실수형'으로 선언했으므로 터미널에도 실수형으로 입력해야 함에 주의하자!

지금처럼 입력해야 할 변수가 여러 개일 때는 각각의 변수를 띄어쓰기를 통해 구분한다. '100.0 80.0 55.0' 이런 식으로 띄어쓰기를 통해 score1, score2, score3 세 변수에 대한 값을 입력한 뒤 엔터를 쳐주면 위와 같이 결과를 출력해준다.

만일 입력 받을 변수들을 띄어쓰기로 구분하는 대신, 여러 줄에 걸쳐서 입력을 받고 싶다면 아래와 같이 read문을 여러 개 사용하면 된다.(4_example_4.f90)

Comments