Chương trình viết bằng Fortran chuẩn đảm bảo tính khả chuyển portable , chạy ổn định trên các hệ máy tính khác nhau có cài đặt trình biên dịch Fortran.. Phần trình b
Trang 1LA VĂN HIỂN
GIỚI THIỆU
NGÔN NGỮ LẬP TRÌNH
Fortran 90
Tài liệu lập trình căn bản
dành cho sinh viên :
T ài liê ̣u được trình bày dưới hình thức tự học
B ao gòm các no ̣i dung cơ bản nhát của Fortran 90
T hực hành la ̣p trình ở cuói mõi chương
K ết hợp phần mềm GNUPLOT để vễ đò thị
05/2020
Trang 2Lời nói đầu
Tài liệu “ Giới thiệu NGƠN NGỮ LẬP TRÌNH Fortran 90 “ được biên soạn nhằm giúp
các bạn sinh viên các khới, ngành khoa học kỹ thuật, khoa học tự nhiên quan tâm đến ngơn ngữ Fortran nắm được phương pháp căn bản để viết văn bản chương trình và chạy các chương trình tính toán bằng Fortran 90, phục vụ yêu cầu học tập , thực hiện các bài tập lớn ,luận văn tớt nghiệp
Fortran có lịch sử phát triển lâu đời qua nhiều thế hệ máy tính với các phiên bản khác nhau , đến nay vẫn là ngơn ngữ có nhiều ưu thế trong lĩnh vực tính toán sớ Fortran được xem là ngơn ngữ lập trình rất phớ biến đới với các sinh viên, kỹ sư, khoa học gia thuợc các lĩnh vực khoa học , kỹ thuật tại các nước Âu – Mỹ Việc học tập Fortran còn giúp các bạn sinh viên có điều kiện học tập ở nước ngoài , đọc hiểu các chương trình viết bằng ngơn ngữ Fortran được trình bày trong các sách , tài liệu , bài báo nước ngoài
Hai phiên bản Fortran được sử dụng phở biến nhất hiện nay là FORTRAN 77 và Fortran 90 , trong đó phiên bản Fortran 90 được xem là mợt phiên bản quan trọng trong lịch sử phát triển Fortran Fortran 90 có rất nhiều điểm mới so với các phiên bản trước như văn bản chương trình được phép viết theo kiểu “ định dạng tự do “ ( free format ) khác với kiểu “ định dạng cớ định theo cợt “ ( fixed format ) , các cấu trúc điều khiển , xử lý mảng , xử lý vào / ra , chương trình con , cấp phát đợng , đơn vị module …tạo nhiều thuận lợi hơn cho người lập trình Từ kiến thức Fortran 90 , các bạn cĩ thể dễ dàng tiếp cận các phiên bản tiếp theo như Fortran 95 , Fortran 2003 (hỗ trợ lập trình hướng đới tượng ) và Fortran 2008
Tài liệu này sử dụng các trình biên dịch miễn phí như G77/GFortran của GNU Compiler Collection (GCC) và G95 thơng qua phần mềm Force 2.0 là mợt mơi trường phát triển tích hợp ( IDE ) gọn nhẹ dùng để viết văn bản chương trình , tiến hành biên dịch , liên kết và chạy thử chương trình Tài liệu cũng giới thiệu thêm trình biên dịch Fortran g95 và Silverfrost FTN95 Personal Edition để bạn đọc tham khảo , chạy kiểm chứng chương trình
Phần phụ lục giới thiệu tởng quát phần mềm GNUPLOT dùng để vẽ các đờ thị 2D, 3D lấy dữ liệu từ kết quả tính toán của Fortran và mợt chương trình mẫu trong bợ LINPACK dùng để giải hệ phương trình tuyến tính là các ví dụ về các tiện ích miễn phí từ các tở chức phát triển Fortran cung cấp cho người sử dụng Các bạn sinh viên cần tham khảo , sử dụng các chương trình tiện ích rất phong phú đã được lập trình sẵn từ các thư viện Fortran trên Internet , phục vụ cho việc học tập, nghiên cứu
Mặc dù đã cớ gắng rà soát kỹ các nợi dung trong tài liệu cũng như chạy kiểm tra các chương trình ví dụ nhưng khó tránh khỏi có những sai sót hoặc những điểm trình bày chưa phù hợp Tác giả rất mong và xin cám ơn sự đóng góp của các bạn đọc , các bạn sinh viên gần xa để tài liệu ngày càng hoàn thiện hơn
Tháng 12 năm 2019
Phiên bản tháng 5-2020
La Văn Hiển Email : hienbk58@yahoo.com
hienemmc6@gmail.com
Trang 3CHƯƠNG 1 : GIỚI THIỆU CHUNG VỀ NGÔN NGỮ LẬP TRÌNH FORTRAN 1.1 Sơ lược về lịch sử ngôn ngữ lập trình Fortran
1.1.1 Nguồn gốc
Fortran ( hay FORTRAN ) được viết tắt từ cụm từ Mathematical FORmula TRANslating System ( hệ thống dịch công thức toán học ) , là ngôn ngữ lập trình cấp cao đầu tiên , kiểu biên
dịch ( compiler ) , do John Warner Backus ( 1924 – 2007 ) cùng một nhóm kỹ sư điện toán thuộc
tập đoàn công nghiệp máy tính IBM ( International Business Machines ) thiết kế và phát triển từ giữa những năm 50 của thế kỷ XX Ban đầu tên ngôn ngữ được viết là FORTRAN , nhưng xu hướng hiện nay được viết chuẩn hóa thành Fortran ( chỉ viết hoa kí tự đầu )
1.1.2 Phạm vi ứng dụng
Ngôn ngữ lập trình Fortran hiện nay có đầy đủ tính năng của một ngôn ngữ lập trình cấp cao hiện đại, hỗ trợ nhiều kiểu lập trình , có khả năng ứng dụng trên nhiều lĩnh vực khác nhau , tuy nhiên Fortran thích hợp hơn cho các ứng dụng tính toán số trong lĩnh vực khoa học kỹ thuật , khoa học tự nhiên và được xem là ngôn ngữ lập trình rất phổ biến đối với các sinh viên, kỹ sư, khoa học gia thuộc các lĩnh vực nêu trên tại các nước Âu Mỹ
1.1.3 Các phiên bản của ngôn ngữ Fortran
FORTRAN (1957) , FORTRAN II ( 1958) , FORTRAN III (1958) , FORTRAN IV (1961) , FORTRAN 66 (1966) được xem là các phiên bản cũ , lỗi thời
Các phiên bản được sử dụng phổ biến hiện nay là FORTRAN 77 , Fortran 90 (chuẩn hóa năm 1991) và Fortran 95 Phiên bản Fortran 90 , được xem là cột mốc quan trọng trong lịch sử phát triển ngôn ngữ Fortran , có rất nhiều điểm mới so với FORTRAN 77 như văn bản chương trình hay tập tin nguồn ( source code ) được phép viết theo kiểu “ định dạng tự do “ ( free format ) , khác với kiểu “ định dạng cố định theo cột “ ( fixed format ) của các phiên bản trước ; các cấu trúc điều khiển , xử lý mảng , xử lý vào/ra , cấp phát động , chương trình con , đơn vị chương trình module tạo nhiều thuận lợi hơn cho người lập trình Phiên bản Fortran 95 bổ sung một số ít các nội dung mới vào Fortran 90 và bỏ đi một vài điểm lỗi thời vẫn còn giữ trong Fortran 90 Hai phiên bản sau cùng là Fortran 2003 ( hỗ trợ lập trình hướng đối tượng ) và Fortran 2008
Một số công ty, tổ chức phát triển ngôn ngữ Fortran đã cho ra đời nhiều biến thể khác nhau
từ Fortran chuẩn ( Standard Fortran ) bằng cách thêm vào một số chức năng mở rộng Các phiên bản Fortran chuẩn được ANSI ( American National Standards Institute - Viện tiêu chuẩn quốc gia Hoa Kỳ ) và ISO ( International Organization for Standardization - Tổ chức quốc tế về tiêu chuẩn hóa ) xem xét và công bố Chương trình viết bằng Fortran chuẩn đảm bảo tính khả chuyển ( portable ) , chạy ổn định trên các hệ máy tính khác nhau có cài đặt trình biên dịch Fortran
Phần trình bày trong tài liệu này sẽ tập trung vào phiên bản Fortran 90 và sử dụng các trình biên dịch Fortran trên nền hệ điều hành Windows Phần thực hành minh họa các chương trình
Fortran 90 , chúng ta sử dụng phần mềm miễn phí Force 2.0 ,version 2.0.9p , được download tại
trang web :
http://force.lepsch.com/2009/05/downloads.html
Tên tập tin download : Force209G95Setup.exe , kích thước 3,55 MB Phần mềm Force là
một môi trường phát triển tích hợp IDE ( Integrated Development Environment ) gọn nhẹ , có chức năng biên dịch và chạy các chương trình viết bằng FORTRAN 77 hoặc Fortran 90 Force sử dụng trình biên dịch miễn phí G77/GFortran của GNU Compiler Collection (GCC) và G95
Khi cài đặt xong , chúng ta mở file Force2.exe , sẽ thấy hiển thị cửa sổ môi trường phát
triển tích hợp IDE của Force 2.0 Trong môi trường này , chúng ta sẽ soạn tập tin nguồn ( văn bản chương trình dạng text ) , tiến hành biên dịch , liên kết ( lệnh compile ) và nếu không có lỗi sẽ tiến hành chạy thử ( lệnh run ) chương trình
Trang 4Ngoài ra , chúng ta có thể chọn các trình biên dịch Fortran khác như sau để chạy kiểm tra cùng một chương trình nguồn :
Sử dụng môi trường IDE của SilverFrost qua chương trình FTN95 Personal Edition (
chương trình miễn phí phục vụ học tập , chạy test ) với file có tên ftn95_personal.exe ( kích
thước 70, 996 MB ) , được download tại trang web :
Để có thể biên dịch trực tiếp tại cửa sổ Command Prompt của Windows , chúng ta có thể
vào trang web The Fortran Company có địa chỉ như sau để download về trình biên dịch g95 qua một file cài đặt có tên g95-Mingw_201210.exe ( 5381 KB ) :
http://www.fortran.com/the-fortran-company-homepage/whats-new/g95-windows-download/
Phần hướng dẫn sơ lược sử dụng các trình biên dịch này sẽ được trình bày ở cuối chương
1.2 Bộ kí tự của Fortran Văn bản chương trình Fortran ( source code , source file )
1.2.1 Bộ kí tự của Fortran
Bộ kí tự của Fortran bao gồm :
a) Chữ viết thường : từ a đến z ; ( 26 chữ cái tiếng Anh )
b) Chữ viết hoa : từ A đến Z ;
c) Chữ số : 0 đến 9 ; ( 10 chữ số )
d) Kí tự nối chân : _ ; ( underscore )
e) Các kí tự khác : = + - * / ( ) < > , ; : ‘ “ ! & % $ ?
f) Kí tự trống ( blank , space )
Các đơn vị văn bản chương trình như từ khóa ( keyword ) , tên biến , tên hằng , tên chương trình , xâu kí tự , biểu tượng …sử dụng các kí tự trong bộ kí tự Fortran
Kí hiệu các phép toán số học cộng, trừ, nhân, chia được viết : + - * / ; riêng phép lũy thừa sử dụng hai dấu * viết liên tiếp ( ví dụ : 2**3 = 23 = 8 )
1.2.2 Soạn thảo văn bản chương trình ( tập tin nguồn dạng *.f90 )
Để soạn một văn bản chương trình Fortran 90 , có thể dùng trình soạn thảo văn bản Notepad hoặc soạn thảo ngay trong môi trường Force Tập tin nguồn phải được lưu ( save as ) dưới dạng
có phần đuôi là *.f90 , ví dụ hinhtron.f90 , để trình biên dịch có thể nhận dạng được loại tập tin và phiên bản Fortran phù hợp ( Tập tin văn bản soạn theo định dạng cố định có phần đuôi là *.f , được giới thiệu ở cuối chương ; tập tin văn bản của Fortran 95 có phần đuôi là *.f95 )
Fortran 90 không phân biệt chữ hoa với chữ thường trong văn bản chương trình ( case insensative ) trừ việc thể hiện nội dung trong xâu kí tự ( string of characters ) Trong các văn bản chương trình trước đây , người lập trình viết các dòng lệnh bằng chữ IN HOA nhưng xu hướng hiện nay là viết văn bản bằng chữ thường hoặc kết hợp cả hai
1.3 Phương thức chung tạo tập tin thi hành (executable file) từ văn bản chương trình
a) Trong môi trường DOS hoặc Windows , các ngôn ngữ lập trình cấp cao ( như Fortran, C,
Pascal,…) tạo tập tin thi hành có đuôi *.exe theo hai bước :
Trang 5Bước 1 : Biên dịch ( compiling ) tập tin nguồn ( source code , file dạng text do người lập
trình soạn thảo ) sang tập tin đối tượng ( object code ) dạng nhị phân ( binary code )
Bước 2 : Liên kết ( linking ) một hoặc vài tập tin đối tượng và các tập tin thư viện có liên
quan để tạo thành tập tin thi hành được ( executable file )
Tập tin thi hành *.exe sau đó có thể chạy độc lập bên ngoài môi trường IDE
b) Sơ đồ minh họa :
Biên dịch Liên kết
( Compiling ) ( Linking )
Hình 1.1 : Các bước biên dịch và liên kết
1.4 Một ví dụ về chương trình Fortran
Để có một cái nhìn tổng quát về việc soạn thảo tập tin nguồn của Fortran và cách thức biên dịch , liên kết và chạy chương trình trong môi trường Force , chúng ta xem qua một ví dụ đơn giản dưới đây
1.4.1 Văn bản chương trình tính chu vi và diện tích hình tròn
Trong cửa sổ Force , vào menu File , chọn New Khung cửa sổ soạn thảo hiện ra và con trỏ
xuất hiện ở dòng đầu tiên Chúng ta vào nội dung văn bản chương trình như dưới đây và lưu tập
tin dưới tên hinhtron.f90 ( cần tạo trước một thư mục chứa các tập tin văn bản Fortran để tiện việc
thực hành ,ví dụ thư mục có tên D :\work ) Văn bản chương trình được xem là một tập các chỉ thị ( tập lệnh ) được sắp xếp theo một thứ tự từ trên xuống dưới và được viết theo các quy tắc về
cú pháp rất chặt chẽ của ngôn ngữ Fortran nhằm ra lệnh cho máy tính thực hiện một nhiệm vụ xác định nào đó
Phần đánh số dòng trong cửa sổ soạn thảo là tự động nhằm giúp người lập trình dễ dàng tham chiếu đến các dòng lệnh Khi chúng ta soạn thảo tập tin nguồn , các từ khóa trong văn bản , các dòng ghi chú và một số đơn vị cú pháp trong văn bản sẽ tự động có màu thích hợp giúp chúng
ta dễ phân biệt các đối tượng khác nhau trong văn bản Trong văn bản ví dụ dưới đây , các từ khóa ( keyword ) của Fortran được in đậm
program HinhTron
implicit none
! phan khai bao
real :: r,chuvi,dientich ! khai bao bien
real, parameter :: pi = 3.14159 ! khai bao hang pi
! phan tinh toan
print*,'Cho biet ban kinh r :' ; read*,r
Program.exe File1.f90
Trang 6chuvi = 2.0*r*pi
dientich = r*r*pi
! ket qua
print*,'Chu vi : ',chuvi
print*,'Dien tich : ',dientich
read* ! tam dung man hinh de xem ket qua
end program HinhTron
Hình 1.2 : Soạn thảo tập tin nguồn trong cửa sổ Force
1.4.2 Biên dịch, liên kết và chạy chương trình hinhtron.f90
Bước biên dịch,liên kết : chọn menu Run , chọn mục Compile (phím tắt Ctrl – F9) Khi
biên dịch , liên kết xong , nếu không có lỗi cửa sổ Process hiển thị như sau :
Hình 1.3 : Cửa sổ Process thông báo việc biên dịch hoàn tất , không có lỗi
Trang 7Trường hợp xảy ra lỗi cú pháp ( syntax error ) sẽ có thông báo thích hợp tại cửa sổ phía dưới vùng soạn thảo để hướng dẫn người lập trình chỉnh sửa lại tập tin nguồn , sau đó lưu (save )
và chạy lại trình biên dịch Ví dụ dòng thứ bảy của văn bản trên , chúng ta viết read , r thay vì viết đúng là read*, r Cửa sổ Process sẽ báo lỗi và có thông báo chỉ ra vị trí ( dòng ) bị lỗi
In file D:\work\hinhtron.f90:7 print*,'Cho biet ban kinh r :' ; read,r Error: Syntax error in READ statement at (1)
Hình 1.4 : Cửa sổ Process thông báo lỗi và vị trí có lỗi cú pháp
Bước chạy thử tập tin thi hành : chọn menu Run, mục Run ( phím tắt F9) Máy tính sẽ tạm
thời thoát khỏi môi trường Force và cho chạy tập tin hinhtron.exe trên một cửa sổ màu đen riêng
Tại đây , con trỏ chương trình sẽ dừng lại sau câu Cho biet ban kinh r : và đợi chúng ta nhập một
giá trị trực tiếp từ bàn phím ( ví dụ 3.0 ) và nhấn phím Enter để xác nhận Các kết quả tính toán được hiển thị trên màn hình Sau khi xem xong kết quả , chúng ta nhấn phím Enter để thoát khỏi cửa sổ chạy chương trình và trở về môi trường IDE của Force
Hình 1.5 : Kết quả chạy tập tin hinhtron.exe
Để chạy file *.exe trực tiếp trong thư mục có lưu file nguồn *.f90 như ví dụ trên , trên thanh
menu chọn Options , chọn Environment Options , chọn tiếp thẻ Run , chọn Run with EXE
(direct ) Trường hợp chọn Console , chương trình sẽ được chạy trên cửa sổ cmd ( command prompt ) của Windows
Hình 1.6 : Chọn cửa sổ chạy chương trình đã được biên dịch và liên kết
Trường hợp có lỗi khi chạy chương trình ( run time error ) hoặc kết quả không đúng với kết quả mà chúng ta đã biết trước ( chạy test chương trình ) thì cần quay lại văn bản chương trình để
Trang 8rà soát lại thuật giải , các biểu thức tính toán , nội dung các câu lệnh để chỉnh sửa , sau đó thực hiện lại việc biên dịch, liên kết và chạy chương trình như trên
Để lưu kết quả của cửa sổ màu đen này vào file Word hoặc Notepad ,chúng ta để con trỏ
lên thanh tiêu đề hinhtron.exe và bấm phím phải con chuột , khi xuất hiện menu ,chọn Edit , chọn Select All , nội dung chọn được chiếu sáng , bấm Enter ( tương đương lệnh Copy) để đưa nội dung này vào clipboard sau đó dán vào file Word đã mở sẵn ( trong Word bấm phím phải chuột , chọn Paste hoặc dùng tổ hợp phím Ctrl – V )
Hình 1.7 : Cách đưa nội dung kết quả của chương trình vào Clipboard
1.5 Giải thích sơ lược văn bản chương trình hinhtron.f90
Chương trình Fortran ví dụ nêu trên chỉ có một đơn vị chương trình là chương trình chính ( main program ) Chương trình Fortran tổng quát thường gồm một chương trình chính và nhiều đơn vị chương trình khác có liên quan ( sẽ được trình bày ở các phần sau )
1.5.1 Cấu trúc tổng quát chương trình chính
program tên_chương_trình ! [ tùy chọn , optional ]
implicit none ! [ tùy chọn , optional ]
! < các câu lệnh không thực thi , khai báo các đối tượng [ biến ,hằng, mảng,…] sử dụng ! trong chương trình , luôn luôn phải đặt trước các câu lệnh thực thi >
! <các câu lệnh thực thi : nhập/xuất dữ liệu , tính toán , so sánh , gọi chương trình con …>
end program tên_chương_trình ! có thể viết đơn giản là end
Các từ program , end program, implicit none thuộc bộ từ khóa ( keyword ) của Fortran
Một số quy định về soạn thảo văn bản chương trình Fortran 90 :
Văn bản chương trình gồm các dòng text , mỗi dòng có không quá 132 kí tự
Nội dung văn bản bên phải sau dấu chấm than ! là phần chú thích , dùng để giải thích các câu lệnh hoặc các phần khác nhau của chương trình nhằm giúp người đọc văn bản hiểu rõ các câu lệnh Trình biên dịch sẽ bỏ qua các chú thích này
Trình biên dịch bỏ qua các dòng trống ( dòng trắng ) trong văn bản chương trình
Thông thường một câu lệnh được đặt trên một dòng văn bản riêng biệt , nếu viết hai hoặc
nhiều câu lệnh trên cùng một dòng thì phải dùng dấu chấm phẩy ; để phân cách các câu lệnh Ví
dụ : print*,'Cho biet ban kinh r :' ; read*, r ! dòng có 2 câu lệnh
Trang 9 Câu lệnh dài nếu cần phải viết trên nhiều dòng liên tiếp thì phải dùng dấu nối & (ampersand) đặt ở cuối dòng và có thể phải đặt ở đầu dòng tiếp theo ( trường hợp xuống dòng ở giữa chừng xâu kí tự thì bắt buộc phải đặt dấu nối & ở đầu dòng tiếp theo ) Ví dụ :
integer :: dai , rong , &
chuvi , dien tich ! khai báo 4 biến kiểu số nguyên
print*, ‘ Tri so chu vi cua &
& duong tron la : ‘ , chuvi ! bắt buộc phải có dấu & ở đầu dòng tiếp theo
dientich = r*r &
*pi ! không cần dấu & ở đầu dòng , nếu có cũng không có lỗi
Số dòng tiếp theo cho phép : 39 dòng ( Thay đổi theo trình biên dịch )
Các khoảng trống được chèn vào giữa các đơn vị văn bản như từ khóa, tên biến , tên hằng , các biểu tượng , vừa dùng để tách rời các đơn vị này ra (quy định bắt buộc ) và vừa giúp cho chương trình dễ đọc Số lượng khoảng trống nhiều ít không ảnh hưởng đến kết quả ( trừ trường hợp xuất hiện trong xâu kí tự ) Tuy nhiên không được chèn khoảng trống vào giữa các kí tự cấu thành một đơn vị văn bản ( từ khóa , tên ) hoặc biểu tượng Ví dụ :
Viết real :: r,chuvi,dientich giống như real :: r, chuvi, dientich ; tuy nhiên lưu ý dấu
:: không được viết tách rời ra thành : : Không được viết re al thay cho real Không được viết
> = thay cho >= ( kí hiệu phép toán so sánh lớn hơn hay bằng )
Nội dung của một xâu kí tự được đặt giữa hai dấu nháy đơn ‘ string ‘ , hoặc hai dấu nháy
kép “ string “ có giá trị như nhau Phải dùng thống nhất một kiểu dấu nháy giữa một xâu kí tự Để
thể hiện It’s a book , chúng ta viết ‘It’’s a book.’ (đặt hai dấu nháy đơn sau chữ t ) , để thể hiện Say “Hello !” , viết ‘Say “Hello !” ‘ hoặc “ Say “”Hello !”” “ Khi nhập xâu kí tự có khoảng trắng
giữa các từ từ bàn phím , phải nhập giữa hai dấu nháy đơn hoặc kép như trên
1.5.2 Câu lệnh implicit none
Câu lệnh implicit none là câu lệnh tùy chọn ( optional ) , được đặt ngay sau từ khóa program , buộc người lập trình phải khai báo rõ ràng ( explicitly ) tất cả các biến , hằng , mảng … tham gia
vào chương trình cùng kiểu dữ liệu tương ứng ( số nguyên, số thực , số phức , kí tự , logic …)
Fortran 90 vẫn chấp nhận quy tắc ngầm định (implicit rule) về kiểu biến số học như các phiên bản trước , nghĩa là có thể không cần phải khai báo biến trong văn bản chương trình , mà chỉ căn cứ vào kí tự đầu của tên biến để biết biến thuộc loại dữ liệu nào : cụ thể những biến có tên bắt
đầu bằng các chữ I,J,K,L,M,N thuộc loại số nguyên mặc định ( integer ) và có tên bắt đầu bằng
các chữ khác thuộc loại số thực mặc định ( real )
Tuy nhiên , việc khai báo đầy đủ các đối tượng như biến, hằng cùng kiểu dữ liệu ngay từ đầu chương trình trước khi sử dụng chúng là một thói quen lập trình tốt , tránh được nhiều sai sót khó phát hiện khi biên dịch chương trình
Trong tài liệu này , chúng ta sử dụng câu lệnh implicit none trong văn bản chương trình
1.5.3 Khai báo biến ( variable)
Biến dùng để lưu trữ dữ liệu , kết quả tính toán trong bộ nhớ máy tính Biến có tên, địa chỉ , kiểu dữ liệu , kích thước , nội dung (giá trị ) ; biến có thể được gán giá trị ban đầu và có thể thay
đổi giá trị này trong quá trình thực thi chương trình Khai báo biến gồm khai báo tên biến ( variable
name ) và kiểu dữ liệu ( data type ) của biến Fortran quy định tên biến là xâu kí tự từ 1 đến 31 kí
tự , chỉ gồm chữ viết (a z , A Z ) , chữ số (0 9) và kí tự nối chân (underscore _ ) , nhưng tên biến phải bắt đầu bằng chữ viết ( letter ) Tên biến không được có khoảng trống chen giữa hoặc kí tự
đặc biệt Ví dụ về tên biến đúng : ChuVi, chu_vi, dientich, NhanVien_01, x, y, z Ví dụ về tên biến đặt sai :1x , dien tich , chu-vi
Fortran không phân biệt chữ hoa với chữ thường trong tên biến Các tên biến : DienTich ,
dientich , Dientich là như nhau Quy tắc đặt tên biến còn được áp dụng cho việc đặt tên ( định
Trang 10danh , identifier ) cho các đối tượng khác như hằng , mảng, tên chương trình ,tên hàm ,tên cấu trúc Tên biến nên được đặt phù hợp với ý nghĩa của đối tượng tham gia chương trình
Ghi chú : Tên biến trong FORTRAN 77 giới hạn trong 6 kí tự , Fortran 2003 là 63 kí tự
Fortran 90 không cấm việc dùng tên các từ khóa ( keyword) để đặt tên ( identifier ) cho các
đối tượng biến, hằng , mảng …như một số ngôn ngữ lập trình khác , nhưng chúng ta nên tránh việc làm này vì dễ gây ra các nhầm lẫn
Câu lệnh real :: r ,chuvi ,dientich , khai báo 3 biến kiểu số thực ( từ khóa real ) có tên
là r, chuvi, dientich ; giữa real và dãy tên biến là dấu :: viết liền nhau Trong trường hợp đơn giản
này dấu :: có thể bỏ mà không ảnh hưởng đến kết quả ( real r, chuvi , dientich ) Lưu ý các tên
biến phải được phân cách bằng dấu phẩy
1.5.4 Khai báo hằng (named constant )
Câu lệnh real , parameter :: pi = 3.14159 , khai báo pi là một hằng kiểu số thực có giá trị
được gán là 3,14159 Ở đây thuộc tính parameter là một từ khóa báo cho biết biến pi được khai
báo là một hằng ,còn gọi là hằng biểu tượng , hằng có tên (named constant ) để phân biệt với hằng
trực tiếp như 1975 , 2.345 Hằng có giá trị không đổi trong suốt chương trình , và không được
viết câu lệnh làm thay đổi giá trị của hằng Lưu ý dấu :: trong trường hợp này bắt buộc phải có Việc khai báo hằng giúp cho chương trình dễ đọc , dễ hiểu câu lệnh và dễ chỉnh sửa giá trị của hằng theo yêu cầu của bài toán Có thể sử dụng chữ in hoa PI , FOOT để đặt tên cho hằng
1.5.5 Xuất ( viết ) dữ liệu ra màn hình ( thiết bị xuất chuẩn )
Câu lệnh print *, <đối tượng xuất ra màn hình > dùng để xuất ( viết ) ra màn hình nội
dung xâu kí tự , giá trị biến , giá trị biểu thức Dấu * cho biết các đối tượng được viết theo định dạng ( format ) tự động ( mặc định ) , phụ thuộc vào cấu trúc máy tính Các đối tượng xuất khác
nhau được phân cách bởi dấu phẩy và được viết ra trên một dòng màn hình (trường hợp dữ liệu
xuất nhiều hơn một dòng , kết quả sẽ được cuộn sang các dòng tiếp theo ) Thực hiện xong lệnh print * , con trỏ tự động xuống đầu dòng tiếp theo
Câu lệnh print* không kèm đối tượng xuất dùng để tạo một dòng trống (blank line) trên
màn hình
Câu lệnh print tổng quát : print format , < list > , trong đó format là phần định dạng hoặc
điều khiển cho các đối tượng xuất theo một sự sắp xếp định trước , list là danh sách các đối tượng xuất ra màn hình
Câu lệnh write (*,*) < đối tượng xuất ra màn hình > có ý nghĩa tương đương lệnh print*,
<đối tượng xuất > Dấu * đầu tiên cho biết kênh xuất là màn hình ,dấu * thứ hai quy định kiểu định dạng là tự động Hai câu lệnh sau là tương đương :
write (*,*) ‘Cho biet ban kinh r : ‘ print*, ’Cho biet ban kinh r : ‘
Ghi chú : lệnh print chỉ dùng để xuất ra màn hình , trong khi lệnh write có nhiều lựa chọn
hơn như dùng để xuất ra màn hình , tập tin , máy in
1.5.6 Nhập dữ liệu từ bàn phím ( thiết bị nhập chuẩn )
Câu lệnh read *, < danh sách tên biến > dùng để nhập giá trị trực tiếp từ bàn phím và gán cho các biến theo thứ tự trong danh sách Khi chương trình thực hiện đến câu lệnh read*, r thì
máy tính sẽ dừng lại chờ chúng ta nhập một giá trị là bán kính r từ bàn phím , ví dụ nhập số 3.0 , xong nhấn Enter để xác nhận Biến r sẽ lấy giá trị 3.0 trong các biểu thức tính toán tiếp theo Câu lệnh read (*,*) < tên các biến > có ý nghĩa tương tự read*,< tên các biến>
Dấu * đầu tiên trong read (*,*) cho biết kênh nhập dữ liệu là bàn phím , dấu * thứ hai cho
biết kiểu định dạng dữ liệu là kiểu tự động Lệnh read (*,*) r read*, r
Nếu x,y,z là ba biến kiểu số thực thì khi gặp câu lệnh read*, x,y,z , chúng ta có thể nhập
giá trị cho từng biến , sau mỗi giá trị nhập phải nhấn Enter ; hoặc có thể nhập cùng lúc ba giá trị
vào trên cùng một hàng nhưng lúc này mỗi số phải được viết cách nhau bằng dấu phẩy hoặc cách nhau một hay vài khoảng trống , xong nhấn Enter để xác nhận việc nhập liệu cho cả ba số
Trang 11Lệnh read* không có đối số làm tạm dừng màn hình chờ chúng ta xem kết quả , xong nhấn
phím Enter để chương trình tiếp tục hoặc kết thúc Nếu không có câu lệnh này thì khi chương trình được thực thi xong , máy tính lập tức quay trở lại môi trường IDE Force làm chúng ta không
kịp xem kết quả Tuy nhiên một số trình biên dịch như Plato (SilverFrost FTN95) lại không cần
câu lệnh này ; khi chương trình chạy xong , xuất hiện câu “ Press return to close window … “ hoặc
“ Press any key to continue “ , lúc này sau khi xem kết quả xong , chúng ta nhấn phím Enter hoặc phím bất kỳ để đóng cửa sổ thực thi chương trình , trở về môi trường soạn thảo
Hai câu lệnh như print*, “ Cho biet gia tri cua x va y : “ ; read *, x,y được dùng kết hợp với nhau để hướng dẫn việc nhập số liệu cho các biến x và y
Lưu ý : khi sử dụng các lệnh print* hoặc read* , phải có dấu phẩy giữa lệnh và đối tượng
xuất đầu tiên ,trong khi dùng write (*,*) và read (*,*) thì không có :
print *, <list> write (*,*) <list>
1.5.7 Câu lệnh gán
Các câu lệnh gán ( assignment ) trong chương trình hinhtron.f90 :
chuvi = 2.0*r*pi ! tính chu vi
dientich = r*r*pi ! tính diện tích
Biểu thức số học bên phải dấu = ( dấu dành cho phép gán , assignment operator ) được tính toán với các giá trị biến, hằng đã biết , sau đó đem gán ( hoặc thay thế ) giá trị này cho biến bên trái Trong phép gán , cần có sự kiểm soát tính tương thích về kiểu dữ liệu giữa hai vế và cần nắm vững quy tắc chuyển đổi mặc định của Fortran về kiểu dữ liệu ( sẽ được trình bày trong các chương
sau ) Dấu phép gán = không phải là dấu bằng theo nghĩa toán học mà phải hiểu là phép thay thế
giá trị được tính từ vế phải cho nội dung (giá trị ) của biến bên vế trái Lưu ý cách viết số thực 2.0 hoặc 2 thay vì viết số 2 là số nguyên ( Nếu viết 2 thì kết quả vẫn tương tự , lúc này Fortran sẽ tự động chuyển đổi số nguyên thành số thực trước khi tính toán biểu thức , chúng ta sẽ tìm hiểu thêm vấn đề này ở chương 3 )
Trong chương trình trên , do chúng ta sử dụng lệnh xuất theo kiểu định dạng (format) tự động cho dữ liệu kiểu real ( số thực độ chính xác đơn ,chỉ thể hiện 6 hoặc 7 chữ số có nghĩa ) nên
các chữ số xuất hiện thêm ở các vị trí cuối là không đáng tin * ( giá trị diện tích đúng ở ví dụ là
28.27431 ) Tuy nhiên nếu sử dụng lệnh print với kiểu định dạng ( format ) phù hợp ( sẽ được trình bày ở các chương sau ) , chúng ta sẽ chủ động và khắc phục được vấn đề này Khi thay các dòng lệnh xuất có định dạng ( dòng 11, 12 ) , được kết quả như sau :
Cho biet ban kinh r :
3.0
Chu vi : 18.84954 ! viết lại dòng 11 : print ‘(a,f10.5)’, ‘Chu vi : ‘, chuvi
Dien tich : 28.27431 ! viết lại dòng 12 : print ‘(a,f10.5)’, ‘Dien tich : ‘, Dien tich
Ghi chú : + Xâu kí tự định dạng ‘(a,f10.5)’ sau từ khóa print dùng để sắp xếp viết ra một xâu kí tự
( kí hiệu địnhdạng là chữ a ) và một số thực được viết dưới dạng dấu chấm tĩnh , chiếm 10 ô ( kí tự ) màn hình , trong đó phần lẻ thập phân chiếm 5 ô ( kí hiệu định dạng là f10.5 ) Số được viết căn lề phải
+ * Sự chuyển đổi tự động giữa hệ thập phân ( người dùng ) và hệ nhị phân ( máy tính ) gây ra sai số do
việc cắt cụt phần đuôi của số thực ( truncation error ) , sai số làm tròn ( round-off error ) trong phạm vi các
chữ số của kiểu dữ liệu Ví dụ số thực real 1/3 được xuất ra : 0.33333334 , số 2/3. 0.6666667 Muốn đạt được kết quả chính xác hơn ( số chữ số có nghĩa nhiều hơn ) , phải chuyển sang sử dụng kiểu dữ liệu có kích thước lớn hơn ( ví dụ số thực chính xác kép : 1/3._8 0.3333333333333333 ) Vấn đề này sẽ được khảo sát ở chương 2
Trang 12+ Lưu ý : Số thực ( real type ) trong máy tính chỉ thể hiện gần đúng kết quả tính toán trên các số thực ,
thực chất các phép tính đều sử dụng số hữu tỉ ( tỉ số của hai số nguyên ) để xấp xỉ số thực , trong khi số nguyên ( integer type , whole number ) được máy tính thể hiện chính xác kết quả khi tính toán các biểu thức số nguyên
1.6 Thực hành
1.6.1 Phân tích câu lệnh
Để làm quen với Fortran , bạn đọc tự phân tích ý nghĩa từng dòng lệnh của chương trình
Fortran chunhat.f90 như sau và chép vào cửa sổ soạn thảo của Force , cần quan sát các màu sắc
do môi trường Force hiển thị để phân biệt các đối tượng khác nhau của văn bản chương trình :
program ChuNhat
! Chuong trinh tinh dien tich hinh chu nhat biet hai canh x,y
implicit none
real :: x,y,dientich ! Co the viet real x,y,dientich
print *,” Tinh dien tich hinh chu nhat “
print *
print ‘(a,$)’, ” Vao gia tri canh x : “ ; read *, x
print ‘(a,$)’, ” Vao gia tri canh y : “ ; read *, y
dientich = x*y ! Tinh dien tich hinh chu nhat
print *
print *,” Dien tich hinh chu nhat co & Kết quả
& gia tri la : “, dientich
end program ChuNhat ! Co the viet don gian la end
Ghi chú :
a.Trong câu lệnh print ‘(a,$)’, ” Vao gia tri canh x : “ , xâu kí tự ‘(a,$)’ là phần định dạng cho đối
tượng xuất ra gồm một xâu kí tự ( kí hiệu định dạng là chữ a ) và sau đó giữ con trỏ không xuống dòng ( kí
hiệu điều khiển là $ ) ,và đợi nhập một giá trị trực tiếp từ lệnh read*, x tiếp theo trên cùng một dòng
b.Trong trường hợp đơn giản chỉ có chương trình chính ( main program ) , chỉ thị kết thúc chương
trình có thể viết đơn giản là end
c Mỗi câu lệnh trong Fortran đều bắt đầu bằng một từ khóa , ngoại trừ câu lệnh gán hoặc câu lệnh
bắt đầu bằng một nhãn ( label ) Cuối một câu lệnh không có dấu chấm phẩy
d Bên trái phép gán , chỉ được phép viết duy nhất một tên biến
e Khi xuống hàng ở giữa chừng xâu kí tự , phải đặt thêm dấu nối & ở đầu dòng tiếp theo
f Dòng đặt tên chương trình program ChuNhat về mặt nguyên tắc có thể bỏ đi , lúc này dòng lệnh
kết thúc là end program hoặc chỉ cần end Tuy nhiên dòng khai báo tên chương trình cần có để cung cấp
thông tin tổng quát về nhiệm vụ của chương trình
1.6.2 Thực hành biên dịch , liên kết và chạy chương trình trong môi trường Force
Sử dụng các lệnh trong menu Run để biên dịch, liên kết ( phím tắt Ctrl – F9 ) và chạy kiểm
tra chương trình ( phím tắt F9)
1.7 Sử dụng trình biên dịch Fortran G95 ( phần tham khảo )
1.7.1 Cài đặt
+ Chạy file g95-Mingw_201210.exe để cài đặt G95 Khi cửa sổ g95_Mingw_Installer
Setup : Installation Folder hiện ra , chúng ta chọn thư mục để lưu chương trình g95 trong ô Destination Folder , ví dụ C : \ g95 hoặc E :\g95 Dung lượng yêu cầu của G95 : 16,1 MB Bấm
nút Install để cài đặt
+ Trong quá trình cài đặt , chúng ta bấm nút Yes hay OK theo đề nghị của chương trình cài đặt ( đặt các biến PATH , cài đặt các tiện ích, thư viện …)
Tinh dien tich hinh chu nhat Vao gia tri canh x : 2.34 Vao gia tri canh y : 4.56 Dien tich hinh chu nhat co gia tri la : 10.6704
Trang 13+ Khi cài đặt thành công , thư mục g95 sẽ có 3 thư mục con là bin , doc, lib và file
uninstall-g95 Chương trình chính uninstall-g95 nằm trong thư mục bin
1.7.2 Sử dụng G95
+ Sau khi soạn xong tập tin nguồn chunhat.f90 ( sử dụng Notepad) và lưu tại thư mục d:\work Để biên dịch và liên kết tập tin này thành tập tin thi hành có tên chunhat và cũng lưu tại d:\work , thực hiện như sau :
Hình 1.8 : Các cửa sổ xuất hiện khi cài đặt chương trình G95
Vào cửa sổ lệnh của windows ( command prompt window ) , đánh dòng lệnh như sau :
c:\> g95 –o d:\work\chunhat d:\work\chunhat.f90 , xong nhấn Enter
g95 : tên chương trình biên dịch
-o d:\work\chunhat : chỉ định tên file kết quả ( output file , executable file ) gồm cả đường dẫn d:\work\chunhat.f90 : tên file nguồn gồm cả đường dẫn
Nếu biên dịch và liên kết thành công , chúng ta có file chunhat.exe và thể chạy test chương trình ngay trong của sổ lệnh ( chạy file output ) c:\> d:\work\chunhat <Enter>
+ Mẫu các câu lệnh dùng để biên dịch , liên kết :
Lệnh g95 –c file.f90 : biên dịch file.f90 thành tập tin đối tượng file.o ; có thể biên dịch nhiều file nguồn cùng một lúc ( Chữ c : compile )
Lệnh g95 –o file file.f90 : biên dịch và liên kết file.f90 thành tập tin thi hành có tên file Lệnh g95 –o file file01.f90 file02.f90 file03.f90 : biên dịch và liên kết cùng lúc nhiều
file nguồn thành một tập tin thi hành có tên file ( Chữ o : output )
Hình 1.9 : Biên dịch , liên kết và chạy chương trình trong cửa sổ lệnh của Windows
Trang 141.8 Trình biên dịch của Silverfrost FTN95 Personal Edition ( phần tham khảo )
Khi chạy file plato.exe xong , cửa sổ môi trường Plato sẽ hiện ra và chúng ta chọn menu
File , chọn tiếp New , cửa sổ New File hiện ra như sau :
Hình 1.10 : Cửa sổ New File
Trong phần Type , chúng ta chọn Free format Fortran file xong nhấn nút Open Khung
soạn thảo hiện ra và chúng ta có thể soạn văn bản chương trình , ví dụ chương trình tính thể tích hình trụ ( V = r2h ) như dưới đây và lưu tập tin có tên hinhtru.f90 Để biên dịch và liên kết ,
chúng ta vào menu Build , chọn Build
Hình 1.11 : Cửa sổ IDE của Plato và vị trí menu Build
Nếu không có lỗi , trong cửa sổ output phía dưới có nội dung sau :
Compiling and linking file: hinhtru.f90
Creating executable: C:\Users\Administrator\Documents\hinhtru.EXE
Compilation and linking completed
Sau đó chúng ta chạy chương trình này : chọn menu Build , lệnh Start Run ( Ctrl – F5)
Một thông báo hiện ra vài giây cho biết về các giới hạn khi sử dụng phiên bản personal edition của phần mềm này trước khi chương trình chạy thực sự
Trang 15Hình 1.12 : Kết quả chạy chương trình hinhtru.f90
Kết quả sẽ hiển thị trên một màn hình màu đen riêng Sau khi xem xong , nhấn Enter để đóng cửa sổ này và quay trở về môi trường Plato Chúng ta cũng có thể copy vào clipboard toàn bộ nội dung trên cửa sổ màu đen này , cách thực hiện tương tự như trong phần mềm Force
Trong menu Build còn có lệnh Compile dùng để biên dịch tập tin nguồn thành tập tin đối tượng Tất cả các hướng dẫn chi tiết sử dụng phần mềm Plato được trình bày trong menu Help
1.9 Văn bản chương trình Fortran 90 viết theo kiểu định dạng cố định ( tham khảo )
Chúng ta xem ví dụ sau đây về cách viết văn bản chương trình Fortran 90 tính chu vi , diện tích hình tròn theo kiểu định dạng cố định ( fixed format )
Hình 1.13 : Văn bản chương trình circle.f viết theo định dạng cố định
Lợi ích : sử dụng , khai thác các văn bản chương trình FORTRAN 77 đã có sẵn , sau đó sử dụng trình biên dịch Fortran 90 để biên dịch và chạy các chương trình này
Quy tắc viết văn bản theo định dạng cố định :
+ Mỗi dòng văn bản chứa 72 kí tự ( từ cột 1 đến cột 72 , được đánh số thứ tự từ trái qua phải ) ,
từ cột 73 trở đi trình biên dịch sẽ bỏ qua
+ Phân chia các khu vực chức năng theo cột như sau :
Trang 16Khu vực viết câu lệnh : từ cột số 7 đến cột 72
Đối với câu lệnh dài cần viết trên nhiều dòng thì mỗi dòng tiếp theo cần đặt dấu * hoặc dấu
& tại cột số 6
Khu vực đặt nhãn ( label ) cho câu lệnh ( áp dụng cho câu lệnh được gắn nhãn ) : từ cột số
1 đến cột số 5 Nhãn có tối đa 5 chữ số ( 1 đến 99999 ) , vídụ : 100 ; 110 ; 200
Dòng có ghi chữ C tại cột số 1 là dòng ghi chú hoặc dòng để trống
+ Ngoài ra đối với Fortran 90 , khi viết văn bản theo định dạng cố định , được phép sử dụng hai nội dung bổ sung như sau :
+ Cho phép viết nhiều lệnh trên cùng một dòng văn bản , dùng dấu chấm phẩy ; để phân cách câu lệnh
+ Cho phép viết nội dung ghi chú sau dấu chấm than !
Lưu ý : do sự không tương thích giữa hai kiểu định dạng , văn bản chương trình viết theo
định dạng cố định phải được lưu với phần đuôi tập tin là *.f , ví dụ circle.f , thay vì *.f90 được
áp dụng cho văn bản viết theo định dạng tự do như trình bày ở các phần trên
Hình 1.14 : Kết quả chạy chương trình circle.exe
1.10 Một số lời khuyên hữu ích khi viết chương trình Fortran
1 Cần đặt tên biến ( variable name ) mang ý nghĩa ( meaningful ) của đối tượng
2 Luôn sử dụng chỉ thị implicit none , khai báo đầy đủ các đối tượng biến, hằng , chương trình
con được sử dụng trong chương trình Phần khai báo phải nằm trước phần sử dụng trong văn bản chương trình
3 Xuất tất cả các giá trị đã nhập ra màn hình , tập tin để kiểm tra ( Echo all input values )
4 Lập danh sách các dữ liệu đầu vào , đầu ra của từng chương trình , bao gồm cả các đơn vị vật lý
đi kèm theo từng dữ liệu
5 Khai báo giá trị của hằng với độ chính xác cao nhất mà máy tính có thể hỗ trợ
6 Luôn kèm theo đơn vị vật lý khi xuất các dữ liệu số
7 Trong trường hợp cần thiết , gán giá trị ban đầu cho tất cả các biến ( Initialize all variables )
Một biến chưa được gán giá trị ban đầu (unassigned variable) sẽ có một giá trị không xác định khi
chương trình bắt đầu được thực thi
*****
Trang 17CHƯƠNG 2 : CÁC KIỂU DỮ LIỆU CỦA FORTRAN
2.1 Các kiểu dữ liệu của Fortran
2.1.1 Khái niệm
Mỗi đối tượng như biến (variable ) , hằng có tên (named constant) gọi tắt là hằng , mảng (array) …trong một đơn vị chương trình Fortran đều phải có một tên riêng hay định danh ( identifier) và gắn liền với một kiểu dữ liệu ( data type ) nào đó như số nguyên, số thực , số phức , logic, kí tự Fortran cung cấp năm kiểu dữ liệu đã được định nghĩa sẵn ( built – in ) như sau :
Integer : kiểu số nguyên
Real : kiểu số thực
Complex : kiểu số phức , gồm phần thực và phần ảo là các số thực
Logical : kiểu logic , chỉ có hai giá trị là đúng ( true.) hoặc sai ( false )
Character : kiểu kí tự ( gồm một kí tự đơn hoặc nhiều kí tự tạo thành xâu (string) ) Các kiểu integer , real , complex thuộc nhóm dữ liệu số ( numeric ) ; các kiểu logical , character thuộc nhóm dữ liệu non-numeric
Đối với mỗi kiểu dữ liệu trên , chúng ta sẽ tìm hiểu cách khai báo biến, hằng , miền giá trị ( range ) cùng kích thước trong bộ nhớ của kiểu dữ liệu , độ chính xác thể hiện của dữ liệu số , các phép tính có liên quan và kết quả trả về
Trên cơ sở các kiểu dữ liệu do Fortran cung cấp , chúng ta còn có thể xây dựng kiểu dữ liệu
do người dùng tự định nghĩa (user defined type hoặc derived data type ) Kiểu dữ liệu hỗn hợp này ( tương tự như kiểu bản ghi trong một số ngôn ngữ lập trình ) bao gồm nhiều thành phần , mỗi thành phần có tên và có kiểu dữ liệu riêng
2.1.2 Khai báo biến và hằng *
a Mẫu chỉ thị khai báo biến :
Tên_kiểu_dữ_liệu :: tên_biến1 , tên_biến2, tên_biến3 ( dấu :: có thể không có )
b Mẫu chỉ thị khai báo biến và gán luôn giá trị ban đầu ( khởi tạo giá trị ) :
Tên_kiểu_dữ_liệu :: tên_biến1 , tên_biến2 = giá trị ( biến2 được gán giá trị
ban đầu Dấu :: bắt buộc phải có )
c Mẫu chỉ thị khai báo hằng ( named constant ) :
Tên_kiểu_dữ_liệu , parameter :: tên_hằng = giá trị ( dấu :: phải có )
Khi khai báo hằng phải dùng từ khóa parameter đi sau tên kiểu dữ liệu
Ghi chú : + *Bước đầu chúng ta chỉ dùng biến đơn ( simple, scalar ) , Fortran còn có biến
mảng (array) gồm nhiều phần tử cùng kiểu , biến kiểu bản ghi ( biến tự tạo ) gồm nhiều thành phần
có kiểu khác nhau .Trong chỉ thị khai báo biến , nếu có gán giá trị ban đầu hay có khai báo thuộc tính như parameter , dấu :: bắt buộc phải có Dấu :: luôn luôn phải viết liền nhau
+ Một biến chưa được gán giá trị ban đầu sẽ có một giá trị không xác định khi chương trình
bắt đầu được thực thi ( unassigned variable ) Nếu chúng ta thử truy xuất giá trị của các biến này , sẽ nhận được các giá trị ‘vô nghĩa ‘ ( garbage value )
2.2 Dữ liệu kiểu số nguyên ( integer , whole number )
2.2.1 Tổng quát về kiểu số nguyên
Kiểu số nguyên dùng thể hiện các số nguyên tương đối trong toán học , bao gồm số nguyên
âm , số nguyên dương Phạm vi ( range) hay miền giá trị hẹp hay rộng của từng kiểu số nguyên tùy thuộc vào việc khai báo thêm tham số phụ có liên quan đến kích thước ( tính bằng byte ) của kiểu dữ liệu trong bộ nhớ
Kiểu số nguyên mặc định ( default ) được khai báo bằng từ khóa integer , chiếm 4 byte
trong bộ nhớ , và có phạm vi biểu diễn :
-2 147 483 648 n 2 147 483 647
Trang 18Ví dụ về cách viết giá trị trực tiếp ( literal constant ) kiểu số nguyên integer :
Số nguyên dương có thể không cần có dấu + ở trước
Ngoài cách viết số nguyên theo hệ thập phân thông thường , Fortran còn cho phép viết số nguyên theo các hệ 2, 8, 16 , sử dụng các kí tự ở đầu là b , o , z như ví dụ sau :
b’1011’ (hệ nhị phân ) o’234’ (hệ bát phân) z’23af’ (hệ thập lục phân)
(Tương ứng với các số 11 , 156 , 9135 trong hệ thập phân )
2.2.2 Phân loại và khai báo các kiểu số nguyên :
Các kiểu số nguyên có kích thước 1,2,4,8 byte , có phạm vi thể hiện (range) như sau :
4 byte -2 147 483 648 2 147 483 647 ( tương tự kiểu integer mặc định )
Bảng 2.1 : Các kiểu số nguyên và phạm vi biểu diễn
Trong ngôn ngữ Fortran 90, việc khai báo kiểu số nguyên có chỉ định rõ miền giá trị ( range
) được thực hiện thông qua tham số kind là một số nguyên Đối với một số trình biên dịch ( bao
gồm Force 2.0 ) số kind được gán trực tiếp bằng với số byte ( kích thước kiểu dữ liệu trong bộ nhớ ) và được khai báo như sau :
integer (kind = 1) :: k ! khai báo biến k là số nguyên 1 byte
integer (kind = 2) :: m ! khai báo biến m là số nguyên 2 byte
integer (kind = 4) :: n ! khai báo biến n là số nguyên 4 byte
integer (kind = 8) :: n8 ! khai báo biến n8 là số nguyên 8 byte
Lưu ý : trong trường hợp này , để viết giá trị trực tiếp của số nguyên 2 byte , ví dụ 213 ,
chúng ta viết như sau : 213_2 ( số nguyên , dấu nối chân , số kind ) Nếu viết 213 thì đây là số nguyên kiểu integer mặc định ( 4 byte ) Để kiểm tra , chúng ta dùng hàm kind (213) và kind
(213_2) sẽ thấy kết quả trả về lần lượt là 4 và 2 ( print *, kind(213) , kind(213_2) )
Chúng ta có thể khai báo số kind thông qua một hằng ik , sau đó gán kind = ik như sau :
integer, parameter :: ik = 2 ! khai báo hằng nguyên ik có giá trị bằng 2
integer (kind = ik ) :: m, n = 15_ik ! khai báo hai biến nguyên m,n có số kind
! là ik = 2 , biến n được gán giá trị đầu là 15 ( viết 15_ik )
! phạm vi biểu diễn : -32768 32767
Khai báo số kind thông qua một hằng có ưu điểm là dễ chỉnh sửa phạm vi ( miền giá trị ) của biến cho phù hợp với yêu cầu của bài toán ( chỉ cần thay đổi giá trị hằng ik )
Tuy nhiên không bắt buộc tất cả các trình biên dịch Fortran đều quy định số kind bằng với
số byte như trên Một số trình biên dịch quy định số kind riêng cho từng kiểu số nguyên (chúng ta
cần tham khảo sổ tay của trình biên dịch cụ thể hoặc chạy test , dùng hàm kind ( ) , để in ra số
kind của các kiểu dữ liệu ) Trong trường hợp này , chương trình nguồn viết bằng Fortran có thể
không tương thích khi chạy trên các trình biên dịch khác nhau khi chúng ta gán số kind bằng một
giá trị cụ thể như trên
Để giải quyết tính khả chuyển ( portable ) về phạm vi thể hiện đối với dữ liệu kiểu số nguyên giữa các trình biên dịch khác nhau ( văn bản chương trình tương thích với các trình biên
Trang 19dịch khác nhau ) , Fortran cung cấp hàm đã được định nghĩa sẵn selected_int_kind ( r ) dùng để
cung cấp số kind cho kiểu số nguyên có phạm vi (range) thay đổi từ – 10r đến +10r do người lập
trình yêu cầu Hàm sẽ trả về số kind nhỏ nhất của trình biên dịch tương ứng với kiểu số nguyên
biểu diễn đầy đủ miền giá trị theo yêu cầu Ví dụ kiểu số nguyên có phạm vi thay đổi từ -104 đến +104 ( -10 000 n 10 000 ) được khai báo như sau để lấy số kind phù hợp :
interger , parameter :: ikind = selected_int_kind (4) ! lấy số kind qua hằng ikind integer (kind = ikind ) :: m,n ! khai báo biến nguyên có số kind = ikind
Lúc này hàm selected_int_kind (4) sẽ trả về số kind là 2 ( sử dụng Force 2.0 )
Lưu ý : nếu miền giá trị của biến do người lập trình yêu cầu không được đáp ứng , hàm
selected_int_kind ( ) sẽ trả về giá trị – 1
integer :: n ! khai bao so nguyen mac dinh
integer (kind = 2) :: n2 ! khai bao so nguyen 2 byte
integer (kind = 4 ) :: n4 ! so nguyen 4 byte
integer (kind = 8 ) :: n8 ! so nguyen 8 byte
! Lay so kind kieu so nguyen theo mien gia tri yeu cau
integer, parameter :: ikind = selected_int_kind (4) ! pham vi -10 4 +10 4
integer (kind = ikind) :: m
! Phan thuc thi
! in ra cac gia tri lon nhat cua tung kieu so nguyen
print*,’Max so nguyen mac dinh :’, huge(n)
print*,’Max so nguyen 2 byte :’, huge(n2)
print*,’Max so nguyen 4 byte :’, huge(n4)
print*,’Max so nguyen 8 byte :’, huge(n8)
print*,’So kind cua m : ‘, ikind
print*,’Max cua kieu so nguyen cua m : ‘, huge(m)
! Phep chia hai so nguyen
print*,’Phep chia 14/4 = ‘ , 14/4
read* ! Tam ngung de xem ket qua
end program testint
Kết quả :
Max so nguyen mac dinh : 2147483647
Max so nguyen 2 byte : 32767
Max so nguyen 4 byte : 2147483647
Max so nguyen 8 byte : 9223372036854775807
So kind cua m : 2
Max cua kieu so nguyen cua m : 32767
Phep chia 14/4 = 3
Qua chương trình nêu trên ,chúng ta nhận xét :
1.Hàm huge (n) trả về số nguyên lớn nhất của kiểu dữ liệu của đối số n
Trang 202.Số kind của biến m ( khai báo phạm vi thể hiện từ -104 đến 104 ) bằng 2 , có kiểu và phạm vi đồng nhất với kiểu số nguyên integer 2 byte
3 Phép chia hai số nguyên 14/4 = 3 cho kết quả là một số nguyên là phần nguyên của phép chia ( Lưu ý : 1/3 cho kết quả là số 0 ) Đây là một đặc điểm cần chú ý khi chia hai số nguyên trong Fortran Nếu muốn lấy phần dư ( dư số ) của phép chia hai số nguyên m/n , chúng ta dùng hàm mod (m,n) , ví dụ r = mod (14,4) ! kết quả r = 2
4 Hàm kind (m) trả về số kind của m Ví dụ : kind (15_2) → 2 , kind (15) 4
Ngoài ra , còn có hàm range (n) trả về số nguyên r thể hiện phạm vi biểu diễn của n :
-10r n 10r Các số nguyên 1 ,2 ,4 ,8 byte có range lần lượt là 2, 4, 9, 18
Ý nghĩa : Số nguyên mặc định có range bằng 9 , thể hiện được số từ 0 đến 10 9 ( một tỷ ) Thực
sự , range của kiểu integer lớn hơn nhiều ( xem range các số nguyên ở phần trên )
Chúng ta xem chương trình sau dùng để in ra số kind của kiểu số nguyên mặc định và số nguyên có phạm vi thể hiện từ -104 đến 104 , chương trình này chạy trên phần mềm FTN95 của SilverFrost ( đã giới thiệu tại chương 1 )
program testint_kind
implicit none
integer :: n = 12 , k ! so nguyen mac dinh
k = selected_int_kind(4) ! so kind cua so nguyen co pham vi tu -104 den 104
print *,'So kind cua kieu integer : ', kind(n)
print *,'So kind cua kieu integer -10**4 den 10**4 : ', k
end
Kết quả :
So kind cua kieu integer : 3
So kind cua kieu integer -10**4 den 10**4 : 2
Nhận xét : số kind của kiểu số nguyên mặc định trong FTN95 là 3 , khác với số kind ( bằng
4 ) của phần mềm Force 2.0 ở trên
Lưu ý : số kind của phần mềm FTN95 đối với số nguyên 1,2,4,8 byte lần lượt là 1,2,3,4
Khi tính toán với các số nguyên , cần kiểm soát giá trị các cận , thể hiện số lớn nhất ( số dương ) và nhỏ nhất ( số âm ) của kiểu dữ liệu , nếu kết quả tính toán ( trung gian hoặc cuối cùng ) vượt ra ngoài miền này ( overflow ) , kết quả trả về là một giá trị không phù hợp
Ví dụ : trong phạm vi số nguyên 2 byte ( giá trị max = 32767 ), nếu m = 15000 và n = 20000
thì kết quả tổng k = m + n sẽ có giá trị trả về là -30536 thay vì 35000 ( do kết quả vượt quá giá trị max của kiểu dữ liệu ) Trong trường hợp này , hiện tượng overflow của số nguyên làm giá trị của biến bị lỗi được cuộn tròn ( wrap around ) sang giá trị âm
2.3 Dữ liệu kiểu số thực ( real )
2.3.1 Tổng quát về kiểu số thực
Kiểu số thực dùng thể hiện các số thực âm, số thực dương thuộc tập hợp các số thực R trong toán học Hai kiểu số thực thông dụng được các trình biên dịch hỗ trợ là số thực có độ chính xác
đơn ( simple precision ) và số thực có độ chính xác kép ( double precision )
Thực chất , máy tính chỉ có thể thể hiện xấp xỉ số thực qua số hữu tỉ ( rational number ) là
tỉ số của hai số nguyên , vì vậy các vấn đề như độ chính xác ( precision , số chữ số thập phân có nghĩa ) , miền giá trị thể hiện (range ) , sai số cắt cụt ( truncation error ) , sai số làm tròn (round-off error ) đều có liên quan đến kích thước ( size , số byte sử dụng trong bộ nhớ ) của kiểu số thực mà người lập trình sử dụng
Trang 212.3.2 Kiểu số thực có độ chính xác đơn
Đây là kiểu số thực mặc định , được khai báo với từ khóa real , chiếm 4 byte trong bộ nhớ
Miền giá trị về giá trị tuyệt đối :
1.2 E-38 ( 1.2 10 -38 ) |x| 3.4 E+38 ( 3.4 10 +38 ) ( giá trị gần đúng )
Số chữ số thập phân có nghĩa ( chính xác ) : 6 hoặc 7 chữ số (các chữ số xuất hiện thêm
ở cuối là không đáng tin )
Cách viết giá trị trực tiếp ( literal constant ) số thực kiểu real :
a) Viết dưới dạng thông thường (dạng dấu chấm tĩnh ) gồm phần dấu ( dấu + có thể bỏ ) ,
phần nguyên , dấu chấm ( thay cho dấu phẩy theo kiểu viết số Việt Nam , bắt buộc phải có ) ,
phần lẻ thập phân sau dấu chấm
Ví dụ : -123.34 -10 0 .35 4.0 +152.18
Cần lưu ý : viết số 10 là số nguyên , viết số 10 là số thực
b) Viết dưới dạng dấu chấm động , hay dạng bậc (lũy thừa cơ số 10) , dùng kí tự E hay e Dạng mExx m*10 xx , trong đó m là phần định trị (gồm dấu , phần nguyên , dấu chấm, phần lẻ thập phân ) , phần mũ xx gồm phần dấu và một số nguyên có hai chữ số nằm trong miền giá
trị cho phép Nếu phần định trị 0.1 m < 1 thì số thực được gọi là chuẩn hóa
Ví dụ : 2.25E+04 2.25e+4 0.225e5 225e+02 22500
-1.5E-01 -15e-02 - 0.15 -123.45 -0.12345E+3 (dạng chuẩn hóa ) Khi cần sử dụng số thực có độ chính xác cao hơn ( số chữ số thập phân chính xác thể hiện nhiều hơn ) , chúng ta sử dụng kiểu số thực có độ chính xác kép hoặc độ chính xác gấp bốn
2.3.3 Kiểu số thực có độ chính xác kép
Số thực có độ chính xác kép được khai báo với từ khóa double precision , chiếm 8 byte trong bộ nhớ hoặc thông dụng hơn ( từ Fortran 90 trở về sau ) là khai báo thông qua số kind phù hợp với trình biên dịch , ví dụ real ( kind = 8 ) Trong trường hợp này , số kind được gán bằng
số byte ( kích thước của kiểu số thực trong bộ nhớ )
Miền giá trị về giá trị tuyệt đối :
2.2 D-308 ( 2.2 10-308 ) |x| 1.8 D+308 ( 1.8 10+308 ) ( giá trị gần đúng )
Số chữ số thập phân có nghĩa ( chính xác ) : 15 hoặc 16 chữ số
Cách viết giá trị trực tiếp số thực kiểu double precision :
Chỉ được viết dưới dạng bậc ( lũy thừa cơ số 10 ) và dùng kí tự D hay d thay cho kí tự E hay e trong cách viết cho kiểu số thực độ chính xác đơn Ví dụ :
4.25D+04 4.25d4 425d+02 ( 42500.)
-2.5D-01 -25d-02 ( - 0.25 )
-36.25 -0.3625D+2 ( dạng chuẩn hóa )
Số thực độ chính xác kép 3.141592654 3.141592654d0 ( do 100 = 1 )
( Thêm hậu tố d0 viết liền sau số thực dạng dấu chấm tĩnh )
Tuy nhiên , từ Fortran 90 trở về sau , giá trị trực tiếp kiểu số thực chính xác kép thường được viết dưới dạng kèm theo số kind phù hợp như trình bày dưới đây
Ghi chú : Do sự chuyển đổi giữa hệ thập phân ( người sử dụng ) và hệ nhị phân ( máy tính
) nên dữ liệu kiểu số thực chỉ thể hiện gần đúng số thực ( do sai số cắt cụt : truncation error hoặc
sai số làm tròn : round-off error ) Độ chính xác của kiểu số thực (số chữ số thập phân có nghĩa được thể hiện ) sẽ tùy thuộc vào kích thước khai báo của kiểu số thực được sử dụng trong bộ nhớ
Trong các phần mềm trợ giúp tính toán như MATLAB , GNU Octave , số thực chính xác kép được
sử dụng là số thực mặc định
Trang 222.3.4 Khai báo kiểu số thực có kèm tham số kind
Từ Fortran 90 trở về sau , để chỉ rõ độ chính xác ( precision ) và miền giá trị ( range ) của
một kiểu số thực , một số trình biên dịch dùng tham số kind , là một số nguyên , và gán số kind
này bằng với số byte , cách khai báo như sau :
real (kind = 4) :: x ! khai báo số thực độ chính xác đơn , giống kiểu real mặc định
real (kind = 8) :: y ! khai báo số thực độ chính xác kép (double precision )
real (kind = 16) :: z ! số thực độ chính xác gấp bốn ( quadruple precision)
Lúc này , các giá trị trực tiếp đối với số thực , ví dụ số thực độ chính xác kép được viết có kèm theo phần đuôi nhận dạng gồm dấu nối chân (underscore) và số kind (là số 8 ) Ví dụ : theo
khai báo như trên , số thực độ chính xác kép 2.45 được viết : 2.45_8 ( giá trị_số kind ) Nếu viết
2.45 thì đây là số thực độ chính xác đơn ( kiểu mặc định ) Chúng ta có thể dùng hàm kind (2.45) và kind (2.45_8) để hiển thị ra số kind tương ứng với kiểu số thực
Để có thể dễ dàng chuyển đổi kiểu số thực trong chương trình , chúng ta khai báo số kind thông qua một hằng nguyên như sau :
integer , parameter :: r = 8 ! khai báo hằng nguyên r = 8
real ( kind = r ) :: x, y = 2._r ! khai báo số thực có kind = r , độ chính xác kép
( Biến y = 2._r được gán luôn giá trị ban đầu với số kind = r )
Tuy nhiên không phải tất cả các trình biên dịch Fortran đều quy định số kind bằng với số
byte như trên Chúng ta cần tham khảo sổ tay của trình biên dịch hoặc chạy một chương trình test
, sử dụng hàm kind ( ) , để biết số kind tương ứng với từng kiểu số thực Ví dụ chúng ta cho chạy dòng lệnh : print*, kind (1.0) , kind (1.d0) để hiển thị ra số kind của kiểu số thực chính xác đơn
và chính xác kép
Việc gán số kind bằng một giá trị cụ thể có thể phù hợp với trình biên dịch này nhưng lại
không phù hợp với trình biên dịch khác Ví dụ đối với trình biên dịch FTN95 của SilverFrost , số
kind của kiểu số thực chính xác đơn là 1 , kiểu chính xác kép là 2
Để đảm bảo tính khả chuyển ( portable ) của văn bản chương trình Fortran giữa các trình biên dịch khác nhau , Fortran cho phép lấy số kind ( là một số nguyên) của kiểu dữ liệu số thực
qua hàm định nghĩa sẵn selected_real_kind (p,r) trong đó đối số p (precision) quy định độ chính xác ( số chữ số thập phân có nghĩa ) và đối số r (range) quy định miền giá trị của kiểu dữ liệu nằm
trong khoảng (-10r , -10-r ) (10 -r , 10r ) Riêng số 0 được xem như giá trị giới hạn khi |x| < giá trị nhỏ nhất của phạm vi biểu diễn của kiểu số thực ( về trị tuyệt đối ) Ví dụ :
+ Đối với kiểu số thực có độ chính xác đơn , khai báo như sau :
integer , parameter :: rs = selected_real_kind (6,37) ! lấy số kind qua hằng rs
! p = 6 và r = 37 , phạm vi ± (10-37 đến 1037) real (kind = rs) :: x, y = 2._rs ! khai báo số thực có kind = rs , chính xác đơn
Hoặc chúng ta chỉ cần khai báo số chữ số thập có nghĩa p trong hàm selected_real_kind :
integer , parameter :: r6 = selected_real_kind (6) ! lấy số kind qua hằng r6
real (kind = r6) :: x, y = 2._r6 ! số thực có 6 chữ số thập phân có nghĩa , tương đương
! số thực chính xác đơn
+ Đối với số thực có độ chính xác kép :
integer , parameter :: rd = selected_real_kind (15,307)
real (kind = rd) :: x, y = 2.3456_rd ! khai báo số thực có kind = rd ,chính xác kép
Hoặc chúng ta chỉ cần khai báo tham số đầu tiên p , xác định số chữ số thập có nghĩa :
integer , parameter :: r15 = selected_real_kind (15) ! lấy số kind số thực có số chữ số
! thập phân có nghĩa là 15 real (kind = r15) :: x, y = 2.3456_r15 ! khai báo số thực có kind = r15 ,chính xác kép
Trang 23+ Đối với số thực độ chính xác gấp bốn ( quadruple precision ) :
integer , parameter :: rq = selected_real_kind (33,4931)
real (kind = rq) :: x, y = 3.456789_rq
Hoặc chỉ khai báo tham số p = 33 :
integer , parameter :: r33 = selected_real_kind (33)
real (kind = r33) :: x, y = 3.456789_r33
Một cách tổng quát, nếu trong chương trình , chúng ta cần hai biến số thực x,y có độ
chính xác là p = 12 chữ số có nghĩa và phạm vi thể hiện ( r = 50) từ ± (10 -50 đến 1050 ), lúc này khai báo như sau :
integer , parameter :: rc = selected_real_kind (12,50) ! lấy số kind qua hằng rc
real (kind = rc) :: x, y=3.1415926_rc ! khai báo số thực có kind = rc
print*,’So kind r :’, rc ! in ra số kind
print*,kind (x) ! rc
Trong trường hợp cụ thể này ( p = 12, r = 50) , số kind sẽ có giá trị bằng 8 ( nếu sử dụng phần mềm Force ) , đồng nhất với kiểu số thực có độ chính xác kép ( 8 là số kind nhỏ nhất của
kiểu số thực mà trình biên dịch hỗ trợ , thể hiện được độ chính xác và phạm vi của kiểu dữ liệu số
do người lập trình yêu cầu qua hàm selected_real_kind (12,50)
Hàm selected_real_kind ( ) sẽ trả về giá trị -1 nếu yêu cầu về độ chính xác của kiểu số thực không được trình biên dịch hỗ trợ , trả về giá trị -2 nếu phạm vi thể hiện không được hỗ trợ
Chúng ta cần nắm vững việc sử dụng hai hàm selected_int_kind ( ) và selected_real_kind ( ) để lấy số kind phù hợp với kiểu dữ liệu ( độ chính xác , phạm vi biểu diễn ) theo yêu cầu của bài toán cần giải quyết
real :: u,v,w ! khai bao so thuc chinh xac don ( mac dinh )
real (kind = 8) :: x,y,z ! khai bao so thuc chinh xac kep
u = 2.34567 ; v = 3.45678
print*, u,v
x = 2.34567_8 ! gia tri truc tiep so thuc chinh xac kep
y = 3.45678_8 ! viet co so kind di kem
print*,x,y
w = u*v ; z = x*y
! Ket qua phep nhan hai so thuc
print*,’Ket qua nhan so thuc chinh xac don :’
Ket qua nhan so thuc chinh xac don :
0.810846519470E+01 ( chính xác chỉ đến 8 số thập phân : 0.81084651 )
Trang 24Ket qua nhan so thuc chinh xac kep :
0.810846514260D+01 ( chính xác )
Kết quả trên cho thấy chúng ta cần chọn kiểu số thực phù hợp ( tương ứng với kích thước kiểu dữ liệu , khai báo qua số kind ) khi có yêu cầu tính toán với độ chính xác cao
Ghi chú : Trong lệnh print , kí hiệu định dạng ‘(d20.12)’ quy định số thực độ chính xác kép
được thể hiện ( viết ra ) theo dạng bậc trong 20 ô (kí tự ) trong đó phần lẻ thập phân ( thuộc phần định trị ) chiếm 12 ô Tương tự cho kí hiệu ‘(e20.12)’ áp dụng cho số thực độ chính xác đơn
integer,parameter :: rs = selected_real_kind(6,37) ! lay so kind do chinh xac don
integer,parameter :: rd = selected_real_kind(15,307) ! so kind do chinh xac kep
integer,parameter :: rc = selected_real_kind(15,50) ! so kind theo yeu cau
real (kind = rs) :: x ! khai bao so thuc chinh xac don
real (kind = rd) :: y ! so thuc chinh xac kep
real (kind = rc) :: z ! so thuc co do chinh xac va mien gia tri theo yeu cau
! In so kind rs,rd,rc
print*,’So kind : ‘
print*,rs,rd,rc
! In mien gia tri Min Max cua kieu du lieu
print*,’Mien gia tri min max :’
print’(2e20.8)’, tiny(x), huge(x)
print’(2e22.8e4)’, tiny(y), huge(y)
print’(2e22.8e4)’, tiny(z), huge(z)
Mien gia tri min max :
0.11754944E-37 0.34028235E+39 ! kiểu số thực chính xác đơn
0.22250739E-0307 0.17976931E+0309 ! kiểu số thực chính xác kép
0.22250739E-0307 0.17976931E+0309 ! kiểu số thực theo yêu cầu
Trong chương trình trên chúng ta có sử dụng hàm tiny(x) và huge(x) để lấy giá trị nhỏ nhất
và lớn nhất của kiểu dữ liệu của x ( về giá trị tuyệt đối )
Lệnh print ‘(2e20.8)’ có phần định dạng kiểu bậc cho 2 số thực , mỗi số thực chiếm độ rộng
20 ô trong đó phần lẻ thập phân chiếm 8 ô
Lệnh print ‘(2e22.8e4)’ có phần định dạng kiểu bậc cho 2 số thực , mỗi số thực chiếm độ rộng 22 ô trong đó phần lẻ thập phân chiếm 8 ô Số chữ số phần mũ là 04 ô
Trang 25Chúng ta thấy số kind của kiểu dữ liệu số thực lấy qua hàm selected_real_kind (15,50) với yêu cầu 15 chữ số có nghĩa và miền giá trị ( về trị tuyệt đối ) thể hiện từ 10-50 đến 1050 là 8, đồng nhất với số kind của kiểu số thực có độ chính xác kép
Khi tính toán với kiểu dữ liệu số thực cần chú ý đến miền giá trị thể hiện của kiểu dữ liệu , nếu kết quả tính toán ( trung gian hoặc sau cùng ) vượt quá giới hạn trên ( về trị số tuyệt đối ) sẽ có lỗi overflow ( tràn số ) , kết quả trả về là Inf ( Infinity ) , hoặc nhỏ hơn giới hạn dưới về trị tuyệt đối ( underflow) kết quả trả về là số 0 Hình vẽ dưới đây thể hiện trục số thực toán học (liên tục ) và các miền giá trị ( phân bố rời rạc ) của kiểu dữ liệu số thực (các đoạn vẽ nét đậm )
-Inf 0 min max +Inf
Hình 2.1 : Trục số thực và miền giá trị kiểu dữ liệu số thực
Ví dụ về cách thể hiện (trả về ) các giá trị đặc biệt của Fortran 90 Hàm mũ ex : exp (800._8) + Inf ; exp (-200.) 0 ; phép chia 0./0 NaN ( not a numer )
2.4 Dữ liệu kiểu số phức ( complex )
2.4.1 Tổng quát về kiểu số phức
Một dữ liệu kiểu số phức bao gồm hai số thực là phần thực và phần ảo Các số thực trong
thành phần tạo nên số phức thông thường có kiểu mặc định real ( độ chính xác đơn ) hoặc real (kind = 8 ) tương ứng với số thực độ chính xác kép
Cách viết giá trị trực tiếp kiểu số phức :
(2.5 , 3.e+1) thể hiện số phức 2.5 + 30.i ; (0 , 1.) thể hiện số i , đơn vị ảo i2 = -1
Lưu ý : giá trị trực tiếp phần thực và phần ảo được viết trong dấu ngoặc đơn và phân cách
bởi dấu phẩy : z = ( phần_thực , phần_ảo ) Khi nhập số phức 1.+2.i từ bàn phím , cần nhập (1.,2.)
xong Enter
2.4.2 Khai báo kiểu số phức
Kiểu số phức được khai báo với từ khóa complex , đây là kiểu số phức mặc định có phần
thực và phần ảo là các số thực kiểu real mặc định
Ví dụ : khai báo biến, hằng kiểu số phức có thành phần thực và ảo có kiểu số thực độ chính xác kép :
integer, parameter :: r = 8 ! khai báo hằng nguyên r = 8
complex ( kind = r ) :: z ! khai báo biến z kiểu số phức có số kind r = 8
! tương ứng với phần thực , phần ảo là số thực có độ chính xác kép
complex , parameter :: i = (0._r,1._r) ! khai báo hằng số phức i , đơn vị ảo
z = (1.2_r, 0.5_r)*i ! kết quả (-0.5 , 1.2 )
Chúng ta xem chương trình sau đây về kiểu số phức mặc định :
program testcom
implicit none
integer,parameter :: r = 4 ! khai bao hang r
complex( kind = r) :: z ! khai bao bien phuc z co so kind = 4
! phan thuc, phan ao la so thuc kieu real mac dinh
complex, parameter :: i = (0._r,1._r) ! khai bao hang so phuc i , don vi ao
z = (1.2_r,0.5_r)*i ! nhan hai so phuc
Trang 26print*,’So phuc z : ‘ , z
print*,’Phan thuc : ‘, real(z) ! ham lay phan thuc
print*,’Phan ao : ‘, aimag(z) ! ham lay phan ao
print*,’So lien hop : ‘, conjg(z) ! ham lay so phuc lien hop
print ‘(a,f8.4)’,’Modun : ‘, abs(z) ! ham lay modun so phuc
read*
end program testcom
Kết quả :
So phuc z : (-0.5,1.2) Phan thuc : -0.5
Các hàm chuẩn của Fortran trong chương trình trên :
Hàm real(z) trả về phần thực của số phức z
Hàm aimag(z) trả về phần ảo của số phức z
Hàm conjg(z) trả về số phức liên hợp z = a – ib của số phức z = a + ib
Hàm abs(z) trả về mô-đun ( suất ) của số phức z
Kí hiệu f8.4 là kiểu định dạng cho số thực với dấu chấm tĩnh có độ dài là 8 ô trong đó phần lẻ thập phân chiếm 4 ô Số được căn lề phải
+ Để lấy góc pha ( argumen) của số phức z , dùng hàm atan2 (y,x) trả về góc =
(Ox,OM) với M ( x,y) : goc_pha = atan2 (aimag(z) , real(z) ) , đơn vị trả về là radian
+ Số phức z còn được gán thông qua hàm cmplx (x,y ) , trong đó x,y là các số thực kiểu real
mặc định ( x,y có thể là các biểu thức số học ) :
complex :: z ! khai báo số phức z kiểu mặc định
real :: x = 1 , y = 3
z = cmplx(x,y) ! z = (1.,3.)
z = cmplx(x/2.,y/3.) ! z = (0.5,1.)
+ Trường hợp x,y là các số thực có độ chính xác kép , chúng ta sử dụng hàm dcmplx (x,y)
để tạo số phức có kind = 8 z = dcmplx (x,y) ! cần khai báo complex (kind = 8) :: z
+ Số phức sử dụng các phép tính + , - , * , / , ** tương tự như số thực
2.5 Dữ liệu kiểu logic ( logical )
Biến thuộc kiểu logic được khai báo với từ khóa logical , chiếm 4 byte trong bộ nhớ ( kiểu logical mặc định ) , và miền giá trị chỉ có hai giá trị là false ( sai ) và true ( đúng ) Lưu ý cách
viết các giá trị false hoặc true phải có dấu chấm đứng trước và sau
Cách khai báo biến kiểu logical :
logical :: test = false ! khai báo biến test kiểu logical , được gán giá trị ban đầu false
Cách khai báo hằng kiểu logical :
logical , parameter :: test = true ! hằng test có giá trị true
Chúng ta xem chương trình testlogic.f90 như sau :
Trang 27program testlogic
implicit none
logical :: test1 = false , test2 = true ! khai bao hai bien logical mac dinh
logical (kind = 1) :: test ! khai bao bien logical kieu 1 byte
print*,test1,’ ‘,test2
test = true
print*,test
print*,’So kind cua test1 : ‘, kind(test1)
print*,’So kind cua test : ‘, kind(test)
So kind cua test1 : 4
So kind cua test : 1
Nhận xét : khi xuất ra màn hình , giá trị true được thể hiện bằng chữ T và giá trị false
được thể hiện bằng chữ F
Kiểu dữ liệu logical còn có thể khai báo thêm tham số kind như kiểu logical ( kind = 1)
chiếm 1 byte trong bộ nhớ như trong ví dụ trên
Biến logical thường dùng để lưu kết quả trả về của một phép so sánh hai biểu thức số học , hai biểu thức kí tự hoặc kết quả tính toán của các biểu thức logic sử dụng các phép toán logic ( đại số Boolean )
2.6 Dữ liệu kiểu kí tự ( character )
2.6.1 Tổng quát về kiểu kí tự
Biến thuộc kiểu kí tự thể hiện hoặc một kí tự đơn ( single character ) như “A” hoặc một xâu kí tự ( string of characters ) như “ Hello Viet Nam ! “ Xâu rỗng không có kí tự nào trong cặp dấu nháy
Giá trị trực tiếp của xâu kí tự được đặt trong cặp dấu nháy đơn ‘xxx’ hoặc cặp dấu nháy kép
“xxx” có giá trị như nhau Chiều dài xâu kí tự ( string length ) là số kí tự giữa hai dấu nháy kể cả
khoảng trống ( kí tự trống ) Xâu rỗng có chiều dài bằng 0 Chữ hoa, chữ thường trong xâu kí tự được phân biệt khi xuất ra màn hình hoặc tập tin kết quả
Lưu ý khi cần thể hiện câu It’s a book :
Nếu dùng dấu nháy đơn : ‘It’’s a book’ ! có hai dấu ‘ liền nhau sau chữ t
Nếu dùng dấu nháy kép : “It’s a book” ! viết bình thường
Để xuất câu Say “ Hello! ” , chúng ta viết ‘ Say “ Hello! ” ‘ hoặc “ Say ““ Hello! ”” “ Biến , hằng , giá trị trực tiếp kiểu kí tự thường được sử dụng để chứa nội dung phần văn bản dạng text xuất hiện trong chương trình như thông báo việc nhập , xuất các biến , thông báo nội dung các phần thực thi chương trình , xâu định dạng (format) kết quả hiển thị , biến lựa chọn dạng kí tự …
2.6.2 Khai báo kiểu kí tự
Biến kiểu kí tự được khai báo với từ khóa character có kèm theo tham số len = n , xác định chiều dài ( cố định ) của biến ( n : số nguyên > 0 , số kí tự tối đa được phép thể hiện kể cả
các kí tự trống của biến , mỗi kí tự chiếm 1 byte trong bộ nhớ ,được đánh số từ 1 đến n , trái qua phải) Nếu tham số len = 1 thì không cần phải đưa vào khai báo ( tương ứng với biến kí tự đơn )
Trong Fortran , biến kiểu kí tự có chiều dài cố định theo chiều dài đã khai báo qua tham số len Nếu nội dung của biến có chiều dài ngắn hơn chiều dài đã khai báo thì sẽ có các khoảng trống
Trang 28chèn bên phải nội dung của biến cho đủ chiều dài khi viết ra giá trị của biến ; ngược lại nội dung của biến sẽ bị cắt cụt cho bằng với chiều dài của biến Chúng ta xem qua 02 chương trình sau về kiểu kí tự :
program testchar
implicit none
! khai bao bien kieu ki tu
character (len = 8) :: city1,city2 ! chieu dai bien 8 ki tu
character yes_no ! the hien chi mot ki tu don ( len = 1)
city1 = ‘Da Lat’ ! them cac khoang trong ben phai khi viet ra
city2 = ‘Nha Trang’ ! bi cat cut
yes_no = ‘Yes’ ! chi lay ki tu dau Y
end program testchar
Trong chương trình testchar :
1 Kí hiệu // là phép nối hai xâu kí tự ; ví dụ : “abc”//”de” trở thành “abcde”
2 Hàm trim ( string) trả về xâu kí tự đã bị cắt bỏ các khoảng trống ở cuối xâu
3 Hàm len( bien) trả về chiều dài của biến ( chiều dài của biến khi khai báo)
Lệnh len(trim(bien)) trả về chiều dài của biến đã cắt bỏ các khoảng trống ở cuối
Hàm len_trim (string) có ý nghĩa tương tự
4 Xâu ‘Da Lat’ khi viết ra màn hình có thêm hai kí tự trống bên phải
5 Xâu ‘Nha Trang’ bị cắt cụt chữ ‘g’ ( do thiếu chỗ thể hiện )
program testchar2
implicit none
character(len = 12) :: dao = “Phu Quoc” , so
print*,’ ’//dao//’ ‘
print*,len(dao) ! chieu dai bien ki tu dao
print*,’ ’//trim(dao)//’ ‘ ! bien dao bi cat khoang trong ben phai
print*,len_trim(dao) ! chieu dai bien bi cat cut khoang trong ben phai
print*,’ ’//adjustl(dao)//’ ‘ ! dua bien ki tu dao ve sat ben trai
print*,’ ’//adjustr(dao)//’ ‘ ! dua ve sat ben phai
print*,dao (1:3) ! xuat 3 ki tu dau
print*,dao(5 : ) ! xuat tu ki tu thu 5 den cuoi
print*,index (dao,”Quoc”) ! vi tri xau con “Quoc”
! trong xau dao
write (so,*) 36 ! bien ki tu so lay gia tri ‘36’
print*,”So : “//so
read*
end program testchar2
Hình 2.3 : Kết quả chương trình testchar2 Trong chương trình testchar2 :
Trang 291.Hàm adjustl( ) đặt biến kí tự về sát bên trái , hàm adjustr( ) đặt biến về sát bên phải
2.Cách viết các xâu kí tự con , trích từ biến xâu kí tự dao :
dao(1:3) thể hiện 3 kí từ đầu của biến dao dao( : 3) dao(5: ) thể hiện từ kí tự thứ 5 đến cuối
dao (2:2) thể hiện đúng kí tự thứ 2
3 Hàm index (dao,”Quoc”) cho biết vị trí xâu con “Quoc” trong biến dao Hàm index
(string,substring) trả về số 0 nếu xâu con substring không được tìm thấy trong xâu string
4 Câu lệnh write (so,*) 36 chuyển đổi số nguyên 36 thành xâu kí tự ‘36’ và gán cho biến
kí tự so
Một số ghi chú về kiểu kí tự :
a.Chúng ta xem khai báo về hằng kiểu kí tự như sau :
character (len = *), parameter :: ThuDo = “Ha Noi”
Khi đặt len = * , chiều dài của hằng kí tự sẽ được để tự do , trình biên dịch sẽ tự xác định theo giá trị được gán cụ thể ở cuối câu lệnh Hàm len(ThuDo) lúc này sẽ có giá trị 6 Lưu ý , khi
khai báo biến kí tự trong chương trình chính , chúng ta không được khai báo len = *
b Khi trích một phần từ xâu kí tự có tên là xaukt , chúng ta dùng cách viết xaukt(m : n)
với m, n là hai ( chỉ ) số nguyên xác định vị trí đầu và cuối , để chỉ xâu kí tự con lấy từ vị trí thứ m đến vị trí thứ n trong xâu xaukt , kể cả các khoảng trống ( tính từ trái qua phải ) Giá trị mặc định : nếu m không có thì xem m = 1 , nếu n không có thì xem n = len(xaukt)
c Chúng ta có thể thay đổi một phần nội dung của xâu kí tự bằng phép gán như sau :
program testxau implicit none character ( len = 12 ) :: ten ten = 'La Van Hien'
print*,’Ten : ’,ten
ten(1:2) ='Le' ! Thay doi 02 ki tu dau
print*,’Ten moi : ’, ten read*
end
2.7 Dữ liệu kiểu hỗn hợp ( Derived data type )
Từ các kiểu dữ liệu cơ sở như đã trình bày ở trên, chúng ta có thể định nghĩa ( xây dựng ) kiểu dữ liệu hỗn hợp gồm nhiều thành phần Mỗi thành phần có tên và có kiểu dữ liệu riêng ( Tương tự kiểu bản ghi gồm các trường ( field ) trong một số ngôn ngữ lập trình ) Chúng ta xem qua ví dụ sau :
real :: HeSo ! he so luong
end type HoSo
! ket thuc khai bao kieu du lieu moi co ten HoSo
type (HoSo) :: nv1 ! khai bao bien nv1 co kieu HoSo
nv1 = HoSo (“Nguyen Van A”, 1976 , 5.75) ! gan toan bo du lieu mot lan
Ten : La Van Hien Ten moi : Le Van Hien
Trang 30nv1%NamSinh = 1976 ! gan rieng truong NamSinh
print*, nv1%HoTen ! viet ra man hinh truong HoTen
print*, nv1%NamSinh ; print*, nv1%HeSo
print*, nv1 ! viet toan bo gia tri cac truong ra man hinh tren mot dong
Lưu ý : để truy xuất một trường , chúng ta viết : tên_biến%tên_trường
2.8 Một số nội dung bổ sung
2.8.1 Cách khai báo kiểu dữ liệu theo phiên bản FORTRAN 77 ( tham khảo )
Do FORTRAN 77 được xem là subset ( một phần ) của Fortran 90 nên các trình biên dịch
Fortran 90 vẫn cho phép sử dụng cách khai báo kiểu dữ liệu theo dạng : tên_kiểu*số_byte Số
byte thể hiện kích thước của kiểu dữ liệu chiếm trong bộ nhớ Tuy nhiên , từ sau Fortran 90 , đây
là cách khai báo kiểu dữ liệu không theo chuẩn ( non standard ) mà nên sử dụng kiểu khai báo qua
tham số kind như trình bày ở các phần trên
Khai báo kiểu số nguyên : integer*1 :: m , n = 25_1 ! số nguyên 1 byte , phạm vi -128 127
integer*2 :: m , n = 40_2 ! số nguyên 2 byte , phạm vi -32768 32767
integer*4 :: m , n = 123 ! số nguyên 4 byte , giống kiểu integer mặc định
Khai báo kiểu số thực : real*4 :: x , y = 2.345 ! số thực 4 byte , giống kiểu real mặc định
real*8 :: x , y = 6.78_8 ! số thực 8 byte , giống kiểu double precision
real*16 :: x.y = 3.567_16 ! số thực 16 byte , giống kiểu quadruple precision
Khai báo kiểu số phức : complex*8 :: z = (1.5 , 3.6) ! giống kiểu complex mặc định có số kind = 4
! phần thực , phần ảo có kiểu real mặc định
complex*16 :: z = (0._8 , 1._8) ! giống kiểu complex có số kind = 8
! phần thực , phần ảo có kiểu double precision
complex*32 :: z = (0._16 , 1._16) ! giống kiểu complex có số kind = 16
! phần thực , phần ảo có kiểu quadruple precision
Khai báo kiểu logical : logical*4 :: test ! kiểu logical 4 byte , giống kiểu logical mặc định
logical*1 :: test ! kiểu logical 1 byte , giống logical ( kind = 1)
Khai báo kiểu kí tự : character*n :: ten1,ten2 ! xâu kí tự có chiều dài là n , tương đương character ( len = n)
! hoặc character (n) Mỗi kí tự chiếm 1 byte character :: ten ! kiểu kí tự đơn , chiều dài len = 1
2.8.2 Một số hàm truy vấn dữ liệu kiểu số thực, số nguyên
Dưới đây là một số hàm dùng để tra cứu thông tin của kiểu dữ liệu số cũng như lấy số kind của kiểu dữ liệu số nguyên, số thực theo các yêu cầu của người lập trình ( đã trình bày chi tiết ở các phần trên ) Đối số x là số thực
Trang 31Bảng 2.2 : Một số hàm truy vấn dữ liệu kiểu số học
epsilon (x) * Trả về số dương nhỏ nhất khi cộng vào 1 sẽ thành một số lớn hơn 1 Còn
gọi là machine epsilon
precision (x) Trả về một số nguyên p thể hiện độ chính xác của kiểu dữ liệu : số chữ số
thập phân có nghĩa
range (x) Trả về một số nguyên r thể hiện phạm vi biểu diễn của kiểu dữ liệu :
-10r x 10rtiny (x) Trả về số dương nhỏ nhất của kiểu dữ liệu ( về trị tuyệt đối )
huge (x) Trả về số dương lớn nhất của kiểu dữ liệu ( về trị tuyệt đối )
kind ( ) Lấy số kind ( là số nguyên ) của kiểu dữ liệu số , kiểu logical
kind (1.0) trả về số kind của số thực chính xác đơn kind (1.d0) trả về số kind của số thực chính xác kép selected_int_kind ( r ) Lấy số kind của kiểu dữ liệu số nguyên có phạm vi từ -10r đén +10r
Hàm sẽ trả về số kind nhỏ nhất mà trình biên dịch hỗ trợ selected_real_kind (p,r)
p,r : nguyên dương
Lấy số kind của kiểu dữ liệu số thực có độ chính xác là p ( số chữ số thập phân có nghĩa ) và có phạm vi thể hiện ( về trị tuyệt đối ) từ 10-r đến 10r Hàm sẽ trả về số kind nhỏ nhất mà trình biên dịch hỗ trợ
* Ghi chú về số epsilon : epsilon thể hiện khoảng cách giữa số 1.0 và số liền kề lớn hơn trong dãy phân bố
các số thực do máy tính thể hiện Có thể in ra các giá trị epsilon cho kiểu số thực chính xác đơn , chính xác kép như sau :
print *, epsilon (1.) 0.119209 e -06 ! ( 2**(-23) )
print*, epsilon (1.d0) 0.222045 e-15 ( chỉ lấy 6 số lẻ thập phân ) ! (2**(-52) )
Ta có biểu thức logical : (1 + epsilon (1.) > 1.) cho giá trị true
* Do số thực được máy tính biểu diễn bằng số gần đúng ( số hữu tỉ ) nên trong lập trình thường thay biểu
thức so sánh bằng nhau giữa hai số thực , dùng toán tử == , ( x == 0.) hoặc ( a == b ) bằng biểu thức abs (x) < epsilon (1.0) hoặc abs (a-b) < epsilon (1.0) , trong đó abs (x ) là hàm lấy giá trị tuyệt đối của số thực
x Nếu biểu thức thay thế này đúng thì xem như x có giá trị bằng 0 hoặc a == b Trong một số bài toán , thay vì sử dụng số epsilon , chúng ta chọn một giá trị giới hạn t nào đó ( ví dụ t = 1.0d-10 , đối với số thực chính xác kép ) , lúc này nếu abs (x) < t thì xem như biểu thức (x == 0.) có giá trị true
* Ngược lại , đối với số nguyên ( integer , whole number ) được máy tính thể hiện giá trị chính xác khi tính toán các biểu thức nguyên , việc so sánh hai số nguyên bằng nhau được viết bình thường qua biểu thức : (
m == n )
* Có thể khai báo việc sử dụng số thực có độ chính xác kép , sử dụng hàm kind ( ) :
integer, parameter :: dp = kind (1.0d0 ) ! Khai báo hằng dp là số kind của kiểu số thực chính xác kép
real ( kind = dp ) :: x,y,z = 2.3456_dp ! Khai báo biến kiểu số thực chính xác kép , gán giá trị ban đầu
2.8.3 Mẫu đầy đủ khai báo biến có kiểu số ( nguyên, thực, phức ) , kiểu logical hoặc kiểu xâu kí tự ( character )
Mẫu khai báo biến có kiểu số hay kiểu logical :
<Kiểu_dữ_liệu> [, <thuộc_tính>] [::] <danh_sách_biến> [ = <giá_trị_đầu> ]
Ghi chú : + Nội dung trong cặp [ ] có thể không có
+ Các thuộc tính ( attributes ) đi sau tên kiểu dữ liệu có thể là : parameter , save, intent, pointer,
target, allocatable, dimension , public , private , external, intrinsic , optional Ngoài thuộc tính
parameter đã biết ( dùng để khai báo hằng ) , các thuộc tính còn lại sẽ được trình bày ở các phần
sau
+ Khi có khai báo thuộc tính hay có gán giá trị ban đầu cho biến , dấu :: bắt buộc phải có
Trang 32+ Các biến trong danh sách được viết cách nhau dấu phẩy
Mẫu khai báo biến thuộc kiểu xâu kí tự :
character [(len = <chiều_dài_xâu>)] [, <thuộc_tính>] [::] <danh_sách_biến> & [ = <giá_trị_đầu> ]
+ Khi khai báo hằng kí tự sử dụng thuộc tính parameter và khai báo len = *
2.9 Thực hành
a Bạn đọc viết chương trình cocbtct.f90 để tính trọng lượng một đoạn cọc bê tông cốt thép có chiều dài cd = 12 m , mặt cắt hình vành khăn có đường kính ngoài dn = 0m50 , đường kính trong dt = 0m32 Trọng lượng riêng của bê tông cốt thép = 2500 kgf/m3 Các số liệu cd,dn,dt được nhập từ bàn phím Số pi = 3.14159 và trọng lượng riêng của bê tông cốt thép được khai báo như hằng số
Phần đáp án đề nghị :
program cocbtct
implicit none
real :: dn,dt,cd,P !Khai bao 04 bien so thuc mac dinh
real , parameter :: pi = 3.14159 , betong = 2500 ! Khai bao 02 hang
print*,’Cho biet duong kinh ngoai : ‘ ; read*,dn
print*,’Cho biet duong kinh trong : ‘ ; read*,dt
print*,’Cho biet chieu dai coc : ‘ ; read*,cd
Hình 2.4 : Mặt cắt cọc BTCT
Hình 2.5 : Kết quả tính toán bài thực hành
b Viết chương trình tạo kiểu dữ liệu mới có tên là Diem gồm 3 thành phần là các
tọa độ Descartes (x , y, z ) Nhập tọa độ cho hai điểm P1 (1,1,3) , P2 (-1,-1,3) và tính khoảng cách giữa hai điểm này bằng công thức :
Trang 33program distance
implicit none
real :: distP1P2 ! Khoang cach P1P2
! Khai bao kieu du lieu Diem
type Diem
real :: x,y,z ! toa do (x,y,z) cua diem theo thu tu
end type Diem
! Ket thuc khai bao kieu du lieu Diem
type (Diem) :: P1,P2 ! Khai bao hai diem P1,P2
distP1P2 = sqrt((P2%x - P1%x)**2 + (P2%y - P1%y)**2 + (P2%z - P1%z)**2 )
print'(a,f8.3)'," Khoang cach P1P2 : " ,distP1P2
end program distance
* Chương trình trên có sử dụng hàm sqrt ( ) dùng để lấy căn bậc hai Đối số là số thực
* Tọa độ (x,y,z) của điểm P1 được viết : P1%x , P1%y , P1%z
* Nếu vào trực tiếp tọa độ từ bàn phím , chúng ta dùng câu lệnh sau :
print '(a,$)',"Vao toa do P1 :"; read*,P1
print '(a,$)',"Vao toa do P2 :"; read*,P2
* Các tọa độ được nhập cách nhau bằng dấu phẩy hoặc khoảng trống ( blank ) theo đúng thứ tự x,y,z
character (len = 5) :: NamNu
end type CongDan
Trang 34end type NhanVien
type (NhanVien) :: NV1 ! khai báo biến NV1 có kiểu NhanVien
NV1 = NhanVien (CongDan ("Tran Van Dung",1990," Nam"), 2015,&
" Ky Thuat " , 5.16 )
print* , 'Thong tin NV1 :',NV1
print*
print* , 'Ly Lich :',NV1%LyLich
print* , 'Nam Sinh :' ,NV1%LyLich%NamSinh
print* , 'He so Luong :',NV1%HeSoLuong
read*
end
Ghi chú : Trong kiểu NhanVien , Trường LyLich có kiểu CongDan đã được định nghĩa ở phần
trên Bạn đọc phân tích văn bản chương trình nêu trên và chạy test
*****
Trang 35CHƯƠNG 3 : PHÉP TOÁN VÀ BIỂU THỨC
3.1 Mở đầu
Trong các chương trước , chúng ta đã làm quen với phát biểu gán theo dạng :
Tên_biến = Biểu thức
Trước tiên , biểu thức bên vế phải được tính theo quy tắc do Fortran quy định và trả về một giá trị thuộc một kiểu dữ liệu nào đó , sau đó giá trị này được gán cho biến bên vế trái
Các loại biểu thức trong Fortran :
Biểu thức số học : kết quả trả về là một số nguyên , số thực hoặc số phức
Biểu thức logic : kết quả trả về là một giá trị kiểu logical , là true hoặc false
Biểu thức kí tự : kết quả trả về là một kí tự đơn hoặc một xâu kí tự
Để thực hiện việc tính một biểu thức , Fortran cung cấp các phép toán số học ( arithmetic operators) , phép toán so sánh hay quan hệ ( relational operators ) , phép toán logic ( logical operators ) , phép nối xâu kí tự ( concatenation operator )
3.2 Các phép toán số học và biểu thức số học
3.2.1 Các phép toán số học
Phép toán số học xuất hiện trong biểu thức số học , thực hiện các phép tính ( operator ) với các toán hạng ( operand ) có thể là các giá trị trực tiếp , biến, hằng, phần tử mảng ,giá trị trả về của hàm … Trong biểu thức số học có thể có các biểu thức số học khác được đặt trong cặp dấu ngoặc ( ) và các hàm Ví dụ về biểu thức số học :
(-b + sqrt (b**2 – 4.*a*c)) /(2.*a) ; b*b + c*c – 2.*b*c*cos (gocA)
Trong biểu thức ví dụ trên có hàm lấy căn bậc hai sqrt ( ) , hàm cosin cos( ) Các hàm này được Fortran cung cấp để sử dụng ngay gọi là intrinsic functions ( danh sách các hàm thông dụng
được liệt kê ở mục 3.6 )
Phép toán hai ngôi :
Phép cộng ( kí hiệu + ) , phép trừ ( - ) , phép nhân ( * ) , phép chia ( / )
Lưu ý : 1) Trong phép chia a/b , yêu cầu mẫu số b 0 Phép chia hai số nguyên m/n cho kết quả là một số nguyên là phần nguyên của phép chia , ví dụ : 5/3 cho kết quả là 1 ; 1/3 cho kết quả
là số 0 , trong khi phép chia hai số thực 1 /3 cho kết quả là 0.3333
2) Hai toán hạng a , b trong phép toán hai ngôi , như a + b , a*b có thể có kiểu hay số kind khác nhau , lúc này Fortran sẽ áp dụng việc chuyển đổi theo quy tắc sao cho hai toán hạng có cùng kiểu dữ liệu và có cùng số kind trước khi thực hiện phép tính
Phép lũy thừa ( kí hiệu ** )
Ví dụ : 23 được viết trong Fortran 2**3 8 Lưu ý với số mũ nguyên âm : 2**(-3) = 1/(2**3) = 1/8 = 0 , do vậy cần viết đúng là 2.**(-3) = 1/8 = 0.125 Fortran cho phép viết a**n với a là một số thực và n là số nguyên dương hoặc âm Trường hợp a**r , với r là số thực thì số thực a phải thỏa điều kiện a > 0 , lúc này a**r được tính qua công thức exp(r*ln(a)) có sử dụng hàm logarit tự nhiên và hàm mũ ex ( theo công thức ar = erLna , với a > 0 )
Phép toán một ngôi :
Phép lấy số đối ( kí hiệu - ) , ví dụ : - x Kí hiệu + cũng có thể xem là phép toán một ngôi
3.2.2 Quy tắc ưu tiên của các phép toán số học
Khi tính một biểu thức số học trong đó có các phép toán khác nhau , Fortran quy định mức độ ưu tiên của các phép toán theo thứ tự từ cao đến thấp như sau :
Trang 36Phép lũy thừa ( ** ) : có mức ưu tiên cao nhất ( được tính trước nhất )
Phép nhân ( * ) , phép chia ( / ) : có mức ưu tiên như nhau
Phép toán một ngôi + và - : có mức ưu tiên như nhau
Phép toán hai ngôi phép cộng ( + ) , phép trừ ( - ) : có mức ưu tiên như nhau
Trường hợp trong biểu thức số học có chứa những biểu thức số học khác được đặt trong cặp dấu ngoặc ( ) , thì giá trị của những biểu thức này được ưu tiên tính trước ( cặp dấu ngoặc trong cùng nhất sẽ được tính trước ) để trở thành một giá trị đơn Do vậy , trong trường hợp nghi ngờ , chúng ta cần dùng thêm các cặp dấu ngoặc để biểu thức trở nên dễ đọc , dễ kiểm soát việc tính toán Lưu ý trong một biểu thức , số dấu ngoặc mở phải bằng số dấu ngoặc đóng
Nếu biểu thức có chứa hàm thì giá trị trả về của hàm cũng được tính trước để trở thành một toán hạng trong biểu thức
Đối với các phép toán có mức ưu tiên như nhau như phép nhân , phép chia hoặc phép cộng
,phép trừ , việc tính các phép toán này được thực hiện từ trái qua phải Riêng phép lũy thừa lại
thực hiện từ phải qua trái
3.2.3 Quy tắc chuyển đổi kiểu dữ liệu trong biểu thức số học và trong phép gán
a) Trong phép toán hai ngôi ( + - * / ) , hai toán hạng có thể có kiểu dữ liệu hay số kind khác nhau Lúc này trình biên dịch sẽ áp dụng quy tắc chuyển đổi ngầm định (implicit ) của Fortran để
hai toán hạng trở thành đồng nhất về kiểu và số kind trước khi thực hiện phép tính
Trong biểu thức có kiểu hỗn hợp ( mixed mode expression ) , sự chuyển đổi tuân theo quy tắc thứ bậc như sau ( chiều mũi tên là chiều chuyển đổi ) :
Kiểu số nguyên Kiểu số thực Kiểu số phức
Kiểu số thực chính xác đơn Kiểu số thực chính xác kép
Như vậy , tùy vào trường hợp cụ thể của biểu thức số học có dữ liệu kiểu hỗn hợp :
+ Các số nguyên sẽ được chuyển đổi thành số thực hoặc số phức
+ Các số thực được chuyển đổi thành số phức
+ Các số thực hay số phức được chuyển đổi thành số cùng kiểu nhưng có số kind cao hơn ( dữ liệu có kích thước lớn hơn )
Ví dụ : integer*real real 4*2.0 4.0*2.0 8.0
real*integer real 3.0*2 3.0*2.0 6.0
complex*<integer hoặc real > complex (2., 1.) *2 (4 , 2 ) real*real(kind=8) real (kind=8) 2.*3._8 6 ! kind (2.*3._8) = 8
Ghi chú : chúng ta cũng có thể chủ động dùng các hàm chuyển đổi kiểu dữ liệu số , như
hàm real ( ) chuyển đổi thành số thực chính xác đơn , hàm dble ( ) chuyển đổi thành số thực chính
xác kép hoặc sử dụng cách viết số có dấu chấm thập phân , hoặc kèm hậu tố d0 ( ví dụ 1.0d0 là số
thực chính xác kép ) để đảm bảo các toán hạng có kiểu đồng nhất trước khi tính toán , ví dụ : phép
tính 2/5 0 nhưng real (2)/real(5) 0.4 hoặc 2.0/5.0 0.4 , 2 /5 0.4 , 2/5 0.4
Trang 37b) Trong một câu lệnh gán , Fortran chấp nhận kiểu của biến bên vế trái có thể khác kiểu với kiểu của biểu thức bên phải Biểu thức số học bên phải được tính trước và trả về một giá trị ( kết quả của biểu thức ) có kiểu dữ liệu và số kind phù hợp với các quy tắc của Fortran như đã nêu
ở trên Giá trị này được chuyển đổi theo kiểu dữ liệu và số kind của biến bên trái và sau đó
gán cho biến này Quy tắc gán này không tuân theo thứ bậc của kiểu dữ liệu số học
Ví dụ : biến_integer = biểu thức có kết quả kiểu real
Giá trị biểu thức bên vế phải ( kiểu số thực ) sẽ bị cắt phần thập phân , sau đó gán cho biến số nguyên bên vế trái
m = 2.3*3.6 ! biến nguyên integer m sẽ có giá trị bằng 8 , là phần nguyên của số 8.28
Ví dụ : biến_real = biểu thức có kết quả kiểu integer
x = 123456789*2 ; print*x 2.4691358e+8 ! biến real x chỉ thể hiện gần đúng số nguyên
246913578 của vế phải và được làm tròn chữ số cuối 8 ( kiểu real chỉ thể hiện được 7 chữ số có nghĩa )
Chúng ta xem chương trình ví dụ sau :
1 program testconver
2 implicit none
3 integer :: m = 5 , n = 2 , p ! biến nguyên
4 real (kind = 4) :: x ,y ! biến số thực chính xác đơn
5 real (kind = 8) :: u = 1.25_8 , v ! biến số thực chính xác kép
6 x = 2.5 + n ! số thực + số nguyên 4.5
7 y = x + m/n ! phép chia ưu tiên tính trước y = 6.5
8 print*,x,y
9 print*, kind (x +m/n) ! hiển thị số kind của biểu thức
10 p = x + m/n ! số thực được gán cho số nguyên bên trái p = 6
11 v = x + u ! số chính xác đơn 4.5 + số chính xác kép 1.25_8
12 print*,p,v
13 print*,kind(x + u) ! hiển thị số kind của biểu thức
14 read*
15 end program testconver
Giải thích các quy tắc chuyển đổi của Fortran về kiểu dữ liệu :
+ Dòng số 6 : x = 2.5 + n Biến nguyên n = 2 được chuyển đổi thành số thực 2.0 , biểu thức bên phải trở thành phép cộng hai số thực và trả về số thực 4.5 gán cho biến x
+ Dòng số 7 : y = x + m/n Phép chia hai số nguyên m/n ( 5/2 ) , cho kết quả là 2, được thực hiện trước do phép chia ưu tiên hơn phép cộng Sau đó số nguyên 2 được chuyển đổi thành số thực 2.0 và cộng vào x được kết quả là số thực 6.5 và được gán cho biến y
+ Dòng số 9 , hàm kind ( x + m/n ) trả về số kind = 4 , chứng tỏ kết quả biểu thức trong ngoặc có kiểu số thực chính xác đơn
+ Dòng số 10 : p = x + m/n Kiểu dữ liệu của biến vế trái ( số nguyên ) khác với kiểu dữ liệu của biểu thức vế phải ( số thực ) Lúc này kiểu của biểu thức bị ép chuyển đổi theo kiểu của biến vế trái và sau đó gán cho biến Biến nguyên p sẽ nhận giá trị là 6 ( sau khi cắt bỏ phần thập phân của số thực 6.5 , là kết quả của biểu thức bên vế phải )
+ Dòng số 13, hàm kind ( x + u ) trả về giá trị kind = 8 , chứng tỏ kết quả biểu thức trong ngoặc có kiểu số thực chính xác kép Biến số thực chính xác đơn x được chuyển đổi thành số thực chính xác kép trước khi phép toán + được thực hiện
c) Quy tắc chuyển đổi kiểu ngầm định như trên có thể làm sai lệch kết quả của chương trình nếu chúng ta không cẩn thận Trong thực hành , đối với câu lệnh gán biểu thức số học cho biến , chúng ta cần tạo thói quen kiểm soát kỹ kiểu dữ liệu , số kind của từng toán hạng trong biểu thức , trình tự thực hiện các phép tính ( dùng thêm các dấu ngoặc khi cần ) , kiểu và số kind của biểu
Trang 38thức vế phải và kiểu , số kind của biến được gán bên vế trái Trong trường hợp cần sự an toàn ,
chúng ta có thể chủ động dùng các hàm chuyển đổi kiểu như real ( ) : đổi số thành số thực mặc
định , dble ( ) : đổi số thành số thực có độ chính xác kép , hoặc dùng cách viết số có dấu chấm
thập phân ( viết 2.0 hay 2 thay cho 2 ) trong các biểu thức có kiểu hỗn hợp
d) Xét 2 phép gán : xd = 1.0d0 + 5.**0.5 và yd = 1.0d0 + 5.d0**0.5d0 trong đó biến xd ,
yd có kiểu số thực chính xác kép Do phép gán thứ 1 bắt đầu bằng biểu thức thành phần 5.**0.5 trong phạm vi số thực chính xác đơn ( kết quả chỉ thể hiện 7 chữ số có nghĩa ) nên khi chuyển sang kiểu số thực chính xác kép và thực hiện phép tính cộng tiếp theo sẽ có kết quả cuối cùng kém chính xác hơn phép gán thứ hai trong đó , ngay từ đầu , sử dụng cách viết kiểu chính xác kép cho biểu thức thành phần 5.d0**0.5d0 Kết quả :
xd = 0.323606801033020D+01 yd = 0.323606797749979D+01 ( yd chính xác hơn xd )
3.3 Biểu thức logic Các phép toán so sánh ( quan hệ ) Các phép toán logic
Biểu thức logic được hình thành từ các toán hạng có kiểu logical ( chỉ có hai giá trị là true hoặc false ) Các toán hạng có thể là biến , hằng , phần tử mảng , giá trị của hàm kiểu logical , biểu thức so sánh và có thể được kết hợp với nhau bằng các phép toán logic Kết quả trả về của
một biểu thức logic là true hoặc false
3.3.1 Các phép toán so sánh
Fortran cung cấp sáu phép toán so sánh dùng để so sánh giá trị ( nguyên, thực ) của hai biểu thức số học với nhau , kết quả trả về của phép so sánh là một giá trị kiểu logical ( true hoặc false
) Cách viết phép toán so sánh dạng chữ của FORTRAN 77 , ví dụ gt ( greater than ) , vẫn được
sử dụng trong Fortran 90
Kí hiệu Kí hiệu theo FORTRAN 77 Ý nghĩa
Bảng 3.1 : Các phép toán so sánh
Lưu ý : các kí hiệu như <= phải viết liền nhau ( không được viết tách ra < = ) Phép so
sánh bằng nhau phải dùng hai dấu bằng liên tiếp == , trong khi dấu = được sử dụng cho phép gán Kí hiệu so sánh khác nhau /= ( có dạng ) Nếu kiểu dữ liệu số nguyên, số thực ở hai vế khác nhau , Fortran sẽ chuyển đổi thành cùng kiểu với kiểu dữ liệu có thứ bậc cao hơn trước khi tiến hành so sánh Chúng ta cũng có thể dùng các hàm ép kiểu hoặc thêm dấu chấm thập phân vào số như đã nói ở trên để hai vế đồng nhất về kiểu dữ liệu trước khi so sánh
Ví dụ : 5.6 <= 3 sẽ có kết quả là false ; ( 2**3 +2 ) == 10 sẽ có kết quả là true
Có thể chủ động viết : 5.6 <= 3.0 ; (2.**3 + 2.0 ) == 10
Đối với số phức , chỉ được sử dụng phép so sánh bằng nhau == và khác nhau /=
Trong Fortran , xét về mức độ ưu tiên tính toán, các phép toán so sánh có mức ưu tiên như
nhau và được thực hiện sau phép toán số học nhưng trước các phép toán logic (xem bảng 3.4 )
Trang 393.3.2 Các phép toán logic
Fortran có bốn phép toán logic hai ngôi dùng để kết hợp hai biểu thức logic với nhau và một phép toán logic một ngôi ( phép phủ định ) tương tự như phép tính đại số Boolean Kí hiệu các phép toán logic như sau :
Kí hiệu Ý nghĩa .not Phép phủ định
.and Phép giao
.or Phép hội
.eqv Phép so sánh tương đương
.neqv Phép so sánh không tương đương Phép loại trừ
Bảng 3.2 : Các phép toán logic
Cho p, q là hai biểu thức logic , các biểu thức not.p ( phủ định của p ) , p and q ( p và q ) , p or q ( p hay q ) , p eqv q ( p tương đương q ) , p neqv q ( p không tương đương q ; p hoặc
q ) có bảng thực trị ( true table ) như sau :
Kí hiệu : t true ; f false
Bảng 3.3 : Bảng thực trị các phép toán logic
Quy tắc về phép toán logic :
a) not p cho giá trị sai khi p đúng và cho giá trị đúng khi p sai
b) p and q chỉ đúng khi p , q cùng đúng , các trường hợp còn lại đều sai
c) p or q chỉ sai khi p , q cùng sai , các trường hợp còn lại đều đúng
d) p eqv q chỉ đúng khi p , q có cùng giá trị ( cùng đúng hoặc cùng sai )
e) p neqv q chỉ đúng khi p , q có giá trị khác nhau ( p đúng , q sai hay p sai, q đúng )
Khi muốn viết phép toán phủ định not của một biểu thức logic , chúng ta phải sử dụng cặp
dấu ngoặc bao lấy biểu thức logic này và viết , ví dụ : not ( p and q ) ; not (a<b and c<d)
do phép toán not có mức ưu tiên cao hơn phép and
Ví dụ về biểu thức logic : a, b, c là ba số thực > 0 và test là biến logical Biểu thức logic trong câu lệnh gán như sau dùng để kiểm tra xem ba số này có thể tạo thành ba cạnh của một tam giác hay không ? :
test = (a+b > c) and (b+c > a) and (a+c > b) , hoặc
test = 2*max (a,b,c) < (a + b +c) ! xem hàm max ( ) ở cuối chương
3.4 Biểu thức kí tự Phép nối xâu kí tự Phép so sánh
Biểu thức kí tự được hình thành từ các giá trị trực tiếp , biến, hằng , phần tử mảng , giá trị hàm …có kiểu kí tự ( character ) Khi viết giá trị trực tiếp một kí tự đơn hay xâu kí tự , chúng ta dùng cặp dấu nháy đơn hoặc cặp dấu nháy kép bao lấy nội dung cần thể hiện
Ví dụ : ‘H’ ; “ Nhap cac he so a,b,c : “
Lưu ý : Fortran có phân biệt chữ hoa với chữ thường trong xâu kí tự
+ Phép toán nối hai xâu kí tự liền nhau được kí hiệu là // ( nối xâu bên phải vào xâu bên trái )
Ví dụ : “Tên : “ // “Nguyen Van A “ sẽ cho kết quả là xâu “ Tên : Nguyen Van A “
Trang 40‘123’//’456’//’789’ ‘123456789’
+ Hàm repeat (string ,n) sẽ trả về xâu string được viết lặp lại n lần nối tiếp nhau Ví dụ :
print*, repeat (‘*’, 20) sẽ viết ra màn hình hai mươi dấu * liền nhau
+ Bảng mã ASCII ( American Standard Code for Information Interchange ) gồm 128 kí tự ( chữ viết, chữ số, kí tự đặc biệt ,kí tự điều khiển …) được sắp xếp theo nhóm và đánh số thứ tự
từ số 0 đến số 127 Khi so sánh hai xâu kí tự có chiều dài khác nhau , xâu ngắn sẽ được chèn thêm vào các khoảng trống bên phải sao cho hai xâu có chiều dài bằng nhau Việc so sánh hai xâu kí tự bằng các phép toán so sánh ( xem 3.3.1 ) được thực hiện bằng cách xét từng cặp kí tự lần lượt từ trái qua phải và tuân theo thứ tự được quy định trong bảng ASCII Trường hợp một xâu là xâu kí tự con của xâu kí tự còn lại thì xâu con được xem là nhỏ hơn
Ví dụ : biểu thức so sánh ‘Fortran’ < ‘Fortran90’ sẽ cho giá trị là true do “Fortran’ là xâu kí tự con của ‘Fortran90’
‘Pascal’ < ‘Fortran’ sẽ cho giá trị là false do kí tự ‘P’ có mã ( số thứ tự ) là 80 và kí tự ‘F’
có mã là 70
‘Fortran’ > ‘ForTran’ sẽ cho giá trị là true do kí tự ‘t’ có mã là 116 và kí tự ‘T’ có mã là
84
+ Để chuyển đổi một số nguyên ( số thứ tự trong bảng ASCII ) thành kí tự tương ứng chúng
ta dùng hàm : achar (integer) Ví dụ : achar (70) ‘F’
+ Ngược lại , để lấy số thứ tự ( mã ) của một kí tự trong bảng ASCII chúng ta dùng hàm :
iachar (character) Ví dụ : iachar (‘P’) 80
+ Khi nhập xâu kí tự ( gồm nhiều từ có khoảng trắng giữa các từ ) từ bàn phím phải nhập giữa cặp dấu nháy đơn hoặc kép ( vd : nhập ‘La Van Hien’ ) Nếu nhập kí tự đơn hoặc từ đơn ( vd : nhập y hay nhập Hien ) thì không cần cặp dấu nháy này
3.5 Bảng tổng hợp các phép toán và mức độ ưu tiên trong Fortran
Các phép toán trong Fortran và mức độ ưu tiên khi thực hiện tính toán trong một biểu thức được tóm tắt trong bảng sau ( được thể hiện từ mức ưu tiên cao đến thấp ) Khi các phép toán số học , logic có mức ưu tiên như nhau thì các phép tính được thực hiện từ trái qua phải , ngoại trừ phép lũy thừa và phép not được thực hiện từ phải qua trái
Kiểu Phép toán
* và / Nhân , chia + và - Cộng , trừ ( phép toán một ngôi ) + và - Cộng , trừ ( phép toán hai ngôi )
So sánh == , /= , < , <= , > , >=
.and
.or
.eqv và neqv
Bảng 3.4 : Các phép toán trong Fortran và mức độ ưu tiên