2

I have written this very simple code in Fortran:

program su
  implicit none
  real ran3
  write(*,*) ran3(0)
end program su

real*8 function ran3(iseed)
  implicit none
  integer iseed
  iseed=iseed*153941+1
  ran3=float(iseed)*2.328+0.5     
end function ran3

I have no problem in compiling it but when I execute the code I get this message:

Program received signal SIGSEGV: Segmentation fault - invalid memory reference.

Backtrace for this error:
#0  0xB76BAC8B
#1  0xB76BB2DC
#2  0xB77BA3FF
#3  0x8048653 in ran3_
#4  0x80486B3 in MAIN__ at der.f90:?
Segmentation fault (core dumped)

Could you please tell why, and how I can solve it?

5 Answers 5

5

I see two problems with the code. The first is the one which I think is the cause of the error. The function ran3 is referenced with the constant 0 as the actual argument, but the corresponding dummy argument iseed is used on the left side of an assignment statement in the function. This is an error: you can't change the value of zero.

The second error is that ran3 returns a real*8 (whatever that may be; it's a non-standard declaration), but in the main program ran3 is declared as being a default real.

The following program and function compile with gfortran 4.7.2.

program su
    implicit none
    real :: ran3

    write(*, *) ran3(0)
end program su

function ran3(iseed)
    implicit none
    integer :: iseed, temp
    real :: ran3

    temp = iseed * 153941 + 1
    ran3 = temp * 2.328 + 0.5
end function ran3
Sign up to request clarification or add additional context in comments.

1 Comment

I've seen cases where this error actually does "change the value of zero". Gangs of fun to track down why a literal zero someplace else in the code is mysteriously not zero. (Be glad it seg faulted.)
2

While there are many good points made above, most of the solutions above defeat the immediate purpose of the function. Notably, random number generators need to return also the "new" value of iSeed in many cases (though the OP's post does not say so explicitly), since often on the next call to the Ran s/r, the "new" value of iSeed is required.

As a basic rule, constants should be passed as Args ONLY to Intent(In) dummy's.

In a sense, the OP was "lucky" to get a segv, since in the (bad) old days it was possible to send the "number" "0" in as "0", but on return "0" everywhere else would contain the value of iSeed and no segv, but lots of bad arithmetic.

A more appropriate solution in this particular case would be NOT to pass a constant at all, but rather as:

program su
implicit none
Integer      :: iSeed
!
iSeed = 0   ! or whatever iSeed is requried
!
write(*, *) ran3(iSeed)

contains

     function ran3(iseed)
        implicit none
        real :: ran3
        integer, intent(InOut) :: iSeed


        iseed = iseed*153941+1
        ran3 = float(iseed)*2.328+0.5     
    end function ran3

end program su

Now, the call/use of Ran3() can be iterative, e.g. via a loop, or Elemental etc, to create a (quasi) random series.

There are other possibilities using External, etc, but that's for another day.

1 Comment

It canot be elemental, Fortran rules prohibit intent(inout) function arguments for elemental functions. It is best to make it a subroutine. One could be tempted to use the function in a compund expressions and it gets very messy to determine what is the correct behaviour of something like ran3(iSeed) + ran3(iSeed).
1

Your code has done nothing to tell the compiler that the declaration

real ran3

refers to the function you define later in your source file. To the compiler you have declared a real variable called ran3. Once the compiler has read the end statement at the end of the program it can bugger off and drink mojitos if it wants to, it is not bound to do any more compilation -- though you might find that some compilers do.

A general rule in structuring Fortran programs is that the compiler must encounter the definition of an entity (variable, function, subroutine, derived-type, what-have-you) before it encounters any use thereof. Your code has broken this rule.

Once the code has declared a real variable it tries, in this statement,

write(*,*) ran3(0)

to access the 0-th element of an array called ran3 and it all ends in tears.

The quick fix would be to move end program su to the end of the source file, and to put a line containing the keyword contains before the definition of the function. You could then delete the declaration real ran3 as the compiler will take care of any linking that needs to be done.

Oh, and while I'm writing, you could do yourself, and those trying to comprehend your code, a favour by paying more attention to formatting what you have posted. Personally (opinion coming up, look away now if you are easily upset) I would fire any programmer who turned in code looking like that on the grounds that anyone who pays so little attention to the small stuff probably doesn't pay much attention to the big stuff either.

1 Comment

As I understand it, it is not necessary to tell the compiler that the declaration of ran3 refers to a function. It has no dimension attribute, so the processor knows it is not an array. From the form of the reference ran3(0) the processor can deduce that it is a function call.
1

If you want, for whatever reason, that your iseed is modified by the function, you should mark it with intent(in out). If you do so, the compiler will trigger an error at compile time when you call the function using a literal constant. If you want to use the parameter just as input, you can mark it as intent(in), and you will get again an error since you are assigning iseed inside your function.

I think it can be a good idea to get the habit of declaring the intent.

Your code could look like

program su
    implicit none

    write(*, *) ran3(0)

contains

  function ran3(iseed)
    implicit none
    real :: ran3
    integer, intent(in) :: iseed
    ! or intent(in out) :: iseed

    iseed = iseed*153941+1
    ran3 = float(iseed)*2.328+0.5     
  end function ran3

end program su

(this won't compile no matter if you use "in" or "in out" as intent, because of what explaned early).

The following instead will compile (and should work, too)

program su
    implicit none

    write(*, *) ran3(0)

contains

  function ran3(iseed)
    implicit none
    real :: ran3
    integer, intent(in) :: iseed

    ran3 = real(iseed*153941+1)*2.328+0.5     
  end function ran3

end program su

1 Comment

(since the "why" is given elsewhere, I've not taken it into consideration)
1

First you have to define idum as an integer.

  program su
  implicit none
  integer idum
  real ran3
  idum = 334
  write(*,*) ran3(idum)
  end program su

then your code will work

1 Comment

Notice the problem of real vs. real*8 is still present even with your patch. See the previous answers for a detailed explanation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.