ตกลงตอนนี้เพื่อเพิ่มคำตอบโดย @ Jerry101 ฉันได้เขียนโค้ดที่แก้ไขแล้ว ในที่นี้ ปัญหาสำคัญคือ IMAXINV
ไม่ได้ประกาศอย่างชัดเจนว่าเป็น REAL
ดังนั้นจึงตีความว่าเป็น INTEGER
(ด้วยเหตุนี้ IMAXINV = 1.0 / IMAX
จึงกลายเป็น 0 เสมอในโค้ดต้นฉบับ) นอกจากนี้ ฉันได้ลบ ISEED
ออกจากบล็อก COMMON
(เนื่องจากถูกส่งเป็นอาร์กิวเมนต์) และใส่คำสั่ง COMMON
อีกคำสั่งใน RANDU
เพื่อแชร์ตัวแปรระหว่างรูทีน ด้วยการปรับเปลี่ยนเหล่านี้ ดูเหมือนว่าโปรแกรมจะทำงานได้อย่างถูกต้อง
PROGRAM RANDOM
COMMON RANDOMNUMBER !<--- ISEED is deleted from here
ISEED = 123
J=1
7 CALL RANDU(ISEED)
J=J+1
WRITE(*,*) RANDOMNUMBER !<--- write to STDOUT for test
IF (J < 100) GOTO 7
END
SUBROUTINE RANDU(ISEED)
real IMAXINV !<--- this is necessary
COMMON RANDOMNUMBER !<--- this is also necessary to share variables
PARAMETER (IMAX = 2147483647, IMAXINV = 1./IMAX)
ISEED = ISEED * 65539
IF(ISEED<0) ISEED = ISEED + IMAX + 1
RANDOMNUMBER = ISEED * IMAXINV
END
ตามที่แนะนำในคำตอบอื่น เราสามารถใช้ FUNCTION
เพื่อส่งคืนตัวแปรโดยตรง จากนั้นเราไม่จำเป็นต้องใช้ COMMON
ดังนั้นโค้ดจะดูสะอาดตาขึ้นเล็กน้อย
PROGRAM RANDOM
ISEED = 123
J=1
7 RANDOMNUMBER = RANDU(ISEED)
J=J+1
WRITE(*,*) RANDOMNUMBER
IF (J < 100) GOTO 7
END
FUNCTION RANDU(ISEED)
real IMAXINV
PARAMETER (IMAX = 2147483647, IMAXINV = 1./IMAX)
ISEED = ISEED * 65539
IF(ISEED<0) ISEED = ISEED + IMAX + 1
RANDU = ISEED * IMAXINV !<--- "RANDU" is the return variable
END
แต่โปรดทราบว่าเมื่อใช้ FUNCTION
ประเภทของตัวแปรส่งคืนควรได้รับการประกาศอย่างชัดเจนในรูทีนการเรียก หากชื่อฟังก์ชันไม่เป็นไปตามกฎโดยนัย (ในโค้ดข้างต้น RANDU
ไม่ได้ถูกประกาศอย่างชัดเจน เนื่องจากถูกตีความว่าเป็น REAL
) อย่างไรก็ตาม กฎการพิมพ์โดยนัยใน Fortran77 มีข้อควรระวังหลายประการ...
หมายเหตุเพิ่มเติม:
เพื่อหลีกเลี่ยงข้อผิดพลาดเหล่านี้ ฉันขอแนะนำให้ใช้ Fortran >=90 (แทนที่จะเป็น Fortran77) เนื่องจากมีคุณลักษณะมากมายในการป้องกันข้อผิดพลาดดังกล่าว ตัวอย่างเช่น โค้ดที่มีการแก้ไขน้อยที่สุดอาจมีลักษณะดังนี้:
module mymodule
contains
subroutine randu ( istate, ran )
implicit none
integer, parameter :: IMAX = 2147483647
real, parameter :: IMAXINV = 1.0 / IMAX
integer, intent(inout) :: istate
real, intent(out) :: ran
istate = istate * 65539
if ( istate < 0 ) istate = istate + IMAX + 1
ran = istate * IMAXINV
end subroutine
end module
program main
use mymodule, only: randu
implicit none
integer :: j, istate
real :: randomnumber
istate = 123 !! seed for RANDU()
do j = 1, 99
call randu ( istate, randomnumber )
write(*,*) randomnumber
enddo
end program
ที่นี่,
implicit none
ใช้เพื่อบังคับใช้การประกาศตัวแปรทั้งหมดอย่างชัดเจน สิ่งนี้มีประโยชน์ในการช่วยหลีกเลี่ยงการพิมพ์ตัวแปรที่ไม่ถูกต้อง (เช่น IMAXINV
ในคำถาม!)
- รูทีนย่อย
RANDU
มีอยู่ใน module
เพื่อให้คอมไพลเลอร์จัดเตรียมอินเทอร์เฟซที่ชัดเจนและการตรวจสอบที่มีประโยชน์มากมาย (กล่าวโดยย่อ module
มีลักษณะคล้ายกับเนมสเปซใน C++) module
ยังสามารถใช้เพื่อกำหนดตัวแปรร่วมได้อย่างปลอดภัยกว่า COMMON
มาก
- ฉันใช้
do
... enddo
โครงสร้างสำหรับการวนซ้ำมากกว่า j
แทนที่จะเพิ่มค่าด้วยตนเองและใช้ goto
แบบแรกนั้นใช้งานง่ายกว่าจริง ๆ และ goto
มีแนวโน้มที่จะทำให้โค้ดอ่านได้น้อยลง...
- ฉันตั้งชื่อไฟล์โปรแกรมเป็น "test.f90" (สังเกตส่วนต่อท้าย .f90) ซึ่งอนุญาตให้มีรูปแบบอิสระ นอกจากนี้ คุณสามารถใช้อักษรตัวพิมพ์เล็กสำหรับตัวแปรได้
- [นอกจากนี้ เนื่องจาก
iseed
เก็บข้อมูลเกี่ยวกับสถานะปัจจุบันของเครื่องสร้างตัวเลขสุ่ม (หลอก) จึงอาจดีกว่าถ้าใช้ชื่อตัวแปรที่แตกต่างกัน (เช่น istate ฯลฯ ) เพื่อเตือนว่าจำเป็นต้องเก็บค่าไว้ในระหว่างการโทร]
ดังนั้น หากคุณสนใจ โปรดพิจารณาใช้ Fortran เวอร์ชันที่ทันสมัยกว่านี้ (แทนที่จะเป็น Fortran77) ซึ่ง ช่วยให้เราสามารถเขียนโค้ดที่ปลอดภัยและมีประสิทธิภาพยิ่งขึ้น :)
person
roygvib
schedule
18.10.2015