cv.cm

코딩 · 프로그래밍 언어

초보자를 위한 Fortran 기초 튜토리얼

10분 분량

핵심 답변

Fortran은 수치 및 과학 계산을 위해 만들어진 컴파일 방식의 정적 타입 언어입니다. 현대 Fortran(Fortran 90 이후 표준)은 자유 형식 소스, 모듈, 동적 배열, 강력한 배열 전체 연산을 갖추었고, 컴파일러가 매우 빠른 기계어로 바꿔 줍니다. 무료 gfortran 컴파일러를 설치하고 코드를 .f90 파일에 저장해 gfortran hello.f90 -o hello로 빌드합니다.

Fortran이란 무엇이며 누가 쓰는가

Fortran(formula translation)은 1950년대에 John Backus가 이끄는 팀이 IBM에서 개발했으며, 지금도 활발히 쓰이는 가장 오래된 프로그래밍 언어 중 하나입니다. ISO에서 표준화하며 주요 개정판은 Fortran 90, 95, 2003, 2008, 2018, 2023입니다. 예전의 고정 형식 스타일(대문자 FORTRAN 77, 7번째 열에서 시작하는 코드)도 여전히 컴파일되지만, 새 코드는 여기서 보여 주는 자유 형식 문법을 사용합니다.

Fortran은 대규모 수치 계산이 중요한 곳에 쓰입니다. 기상과 기후 모델, 전산 유체 역학, 물리와 화학 시뮬레이션, 슈퍼컴퓨터에서 실행되는 많은 프로그램이 그렇습니다. NumPy와 MATLAB 같은 도구 밑에서 쓰이는 선형대수 라이브러리 BLAS와 LAPACK의 레퍼런스 버전은 Fortran으로 작성되어 있습니다.

컴파일러 설치와 Hello, world 실행

GCC에 포함된 gfortran은 무료이며 Linux(gfortran 패키지), macOS(Homebrew gcc), Windows(MSYS2 또는 WSL)에서 사용할 수 있습니다. Intel의 ifx와 LLVM Flang도 다른 컴파일러입니다. Fortran Package Manager(fpm)로 프로젝트를 만들고 빌드할 수 있습니다.

program hello
  implicit none
  print *, "Hello, world!"
end program hello
gfortran hello.f90 -o hello
./hello
#  Hello, world!

print *는 리스트 지향 출력을 사용하며 간격을 알아서 정합니다(맨 앞 공백에 주의하세요). Fortran은 대소문자를 구분하지 않아서 PRINT, Print, print는 같습니다.

변수와 기본 타입

모든 선언은 프로그램 단위의 맨 위, 실행문보다 앞에 둡니다. 항상 implicit none을 쓰세요. 없으면 I부터 N으로 시작하는 선언되지 않은 이름은 조용히 정수가 되고 나머지는 실수가 됩니다.

타입선언 예시설명
integerinteger :: count = 3보통 32비트. 64비트는 integer(int64)
realreal :: x보통 단정밀도
real(real64)real(real64) :: y배정밀도로, 과학 계산에서 흔히 쓰는 선택
complexcomplex :: z = (1.0, 2.0)내장 복소수
logicallogical :: done = .false..true. / .false., 연산자 .and., .or., .not.
charactercharacter(len=20) :: name고정 길이 문자열로, 공백으로 채워짐
program types
  use, intrinsic :: iso_fortran_env, only: int64, real64
  implicit none
  integer :: count = 3
  integer(int64) :: big = 9000000000_int64
  real(real64) :: third
  logical :: done = .false.
  character(len=:), allocatable :: name

  third = 1.0_real64 / 3.0_real64
  name = "Ana"
  print *, count, big, third, done, name
end program types

접미사 _real64는 리터럴의 kind(정밀도)를 정합니다. 없으면 1.0은 단정밀도 상수여서 배정밀도 변수에 대입할 때 자릿수를 잃습니다.

제어 흐름

program control
  implicit none
  integer :: i, total, score

  total = 0
  do i = 1, 10
    if (mod(i, 2) == 0) cycle   ! skip even numbers
    total = total + i
  end do
  print *, "sum of odd numbers:", total   ! 25

  score = 85
  if (score >= 90) then
    print *, "A"
  else if (score >= 80) then
    print *, "B"
  else
    print *, "C"
  end if

  select case (score)
  case (90:)
    print *, "excellent"
  case (80:89)
    print *, "good"
  case default
    print *, "keep going"
  end select
end program control

do i = 1, 10은 양 끝을 포함하며, 선택적인 세 번째 값이 증가폭을 정합니다(do i = 10, 1, -1). cycle은 다음 반복으로 넘어가고 exit는 반복문을 벗어납니다. do while (condition)과, exit를 쓰는 단순한 do도 사용할 수 있습니다. 주석은 !로 시작합니다.

함수, 서브루틴, 모듈

Fortran의 프로시저는 두 종류입니다. 함수는 값을 반환하고 표현식 안에서 쓰이며, 서브루틴은 call로 호출하고 인자를 통해 결과를 돌려줍니다. 프로시저를 모듈에 넣으면 컴파일러가 모든 호출을 검사할 수 있습니다.

module geometry
  implicit none
contains
  function circle_area(r) result(a)
    real, intent(in) :: r
    real :: a
    a = 3.14159 * r**2
  end function circle_area

  subroutine swap(a, b)
    integer, intent(inout) :: a, b
    integer :: tmp
    tmp = a
    a = b
    b = tmp
  end subroutine swap
end module geometry

program demo
  use geometry
  implicit none
  integer :: x, y
  x = 1
  y = 2
  print *, circle_area(2.0)   ! about 12.566
  call swap(x, y)
  print *, x, y               ! 2 1
end program demo

인자는 참조로 전달되므로 프로시저가 호출자의 변수를 바꿀 수 있습니다. intent(in), intent(out), intent(inout)은 각 인자가 할 수 있는 일을 문서화하고 강제합니다. **는 거듭제곱 연산자입니다.

배열: Fortran의 대표 기능

배열은 Fortran의 중심입니다. 인덱스는 기본적으로 1부터 시작하고(어떤 하한이든 가능), 산술은 반복문 없이 배열 전체에 적용되며, 슬라이스는 a(start:end)를 쓰고, 많은 내장 함수가 배열을 직접 다룹니다.

program arrays
  implicit none
  integer :: i
  real :: a(5), b(5)
  real, allocatable :: c(:)
  integer :: m(2, 3)

  a = [(real(i), i = 1, 5)]   ! 1.0 2.0 3.0 4.0 5.0
  b = 2.0 * a + 1.0           ! whole-array arithmetic
  print *, b(2:4)             ! 5.0 7.0 9.0
  print *, sum(a), maxval(b)  ! 15.0 11.0

  allocate(c(10))
  c = 0.0
  print *, size(c)            ! 10
  deallocate(c)

  m = reshape([1, 2, 3, 4, 5, 6], [2, 3])
  print *, m(2, 1)            ! 2 (column-major order)
end program arrays

다차원 배열은 열 우선 순서로 저장됩니다. 메모리에서 첫 번째 인덱스가 가장 빨리 변하며, C와 반대입니다. 속도를 위해서는 가장 안쪽 반복문이 첫 번째 인덱스를 따라 돌게 하세요. 그 밖에 유용한 내장 함수로 matmul, dot_product, transpose, minval, count가 있습니다. 현대 Fortran은 병렬 프로그램용으로 do concurrent 반복문과 코어레이도 제공합니다.

오류 처리

Fortran에는 예외가 없습니다. 실패할 수 있는 문장은 상태 인자를 받습니다. 입출력에는 iostat, allocate에는 stat입니다. 값을 확인하고 대응하세요. 생략하면 오류가 프로그램을 멈춥니다. error stop은 오류와 함께 프로그램을 끝냅니다.

program parse
  implicit none
  character(len=20) :: text
  integer :: n, ios

  text = "abc"
  read(text, *, iostat=ios) n
  if (ios /= 0) then
    print *, "not a number: ", trim(text)
  else
    print *, "n =", n
  end if
end program parse

위처럼 문자 변수에서 읽는 것을 내부 읽기라 하며, 문자열을 숫자로 변환하는 일반적인 방법입니다.

작은 완성 프로그램: 평균과 표준편차

module stats
  implicit none
  integer, parameter :: dp = kind(1.0d0)
contains
  pure function mean(x) result(m)
    real(dp), intent(in) :: x(:)
    real(dp) :: m
    m = sum(x) / size(x)
  end function mean

  pure function std_dev(x) result(s)
    real(dp), intent(in) :: x(:)
    real(dp) :: s
    s = sqrt(sum((x - mean(x))**2) / (size(x) - 1))
  end function std_dev
end module stats

program main
  use stats
  implicit none
  real(dp) :: values(5)
  values = [2.0_dp, 4.0_dp, 4.0_dp, 5.0_dp, 7.0_dp]
  print '(a, f6.3)', "mean    = ", mean(values)
  print '(a, f6.3)', "std dev = ", std_dev(values)
end program main
! mean    =  4.400
! std dev =  1.817

x(:) 선언은 가정 형상 배열이므로 함수가 어떤 길이의 배열도 받을 수 있습니다. pure는 함수에 부수 효과가 없음을 약속합니다. 형식 '(a, f6.3)'은 문자열 뒤에 너비 6, 소수점 이하 3자리의 실수를 출력합니다.

다음 단계

  • fortran-lang.org: 퀵스타트 튜토리얼과 모범 사례가 있는 Learn 섹션.
  • 컴파일러 옵션과 내장 프로시저를 다룬 GNU Fortran(gfortran) 매뉴얼.
  • 프로젝트 생성과 Fortran 표준 라이브러리(stdlib) 같은 라이브러리 사용을 위해 Fortran Package Manager(fpm)를 써 보세요.
  • 배우는 동안에는 경고와 런타임 검사를 켜고 컴파일하세요. 예를 들면 gfortran -Wall -fcheck=all입니다.

자주 묻는 질문

Fortran은 오늘날에도 쓰이나요?

네. Fortran은 과학 계산과 고성능 컴퓨팅, 특히 기후, 기상, 물리, 공학 시뮬레이션에서 여전히 널리 쓰이며 언어 표준도 계속 갱신되고 있습니다.

.f 파일과 .f90 파일의 차이는 무엇인가요?

관례상 .f와 .for 파일은 열 위치가 중요한 FORTRAN 77의 예전 고정 형식 레이아웃을 쓰고, .f90 파일은 자유 형식 소스를 씁니다. .f90 확장자는 Fortran 90뿐 아니라 모든 현대 표준의 자유 형식 코드에 쓰입니다.

Fortran은 왜 빠른가요?

이 언어는 수치 계산을 위해 설계되었습니다. 배열이 내장되어 있고, 별칭 규칙 덕분에 컴파일러가 공격적으로 최적화할 수 있으며, 수십 년간의 컴파일러 작업이 Fortran을 겨냥해 왔습니다. 잘 작성한 C나 C++도 비슷한 속도를 낼 수 있습니다.

Fortran 배열은 0부터 시작하나요, 1부터 시작하나요?

기본적으로 1부터 시작하지만 하한은 원하는 대로 정할 수 있습니다. 예를 들어 real :: a(0:9)는 0부터 9까지 인덱싱되는 요소 열 개를 선언합니다.

코딩 튜토리얼 전체