Skip to content

Implement constants module #99

Open
@certik

Description

@certik

At the very least, it would contain the following constants:

  • pi
  • i_
  • e_

An example implementation: https://github.com/certik/fortran-utils/blob/b43bd24cd421509a5bc6d3b9c3eeae8ce856ed88/src/constants.f90

Note about naming: The convention that we discussed in fortran-utils 10 years ago was that single letter constants contain underscore so that they do not clash with user variables ("e" and "i" are frequently used as loop variables). But we can definitely revisit this and choose a different convention.

Activity

marshallward

marshallward commented on Jan 8, 2020

@marshallward
certik

certik commented on Jan 8, 2020

@certik
MemberAuthor

Julia seems to use Base.im, Base.MathConstants.pi, Base.MathConstants.ℯ, Base.MathConstants.eulergamma, ... (https://docs.julialang.org/en/v1/base/numbers/#Base.im).

certik

certik commented on Jan 8, 2020

@certik
MemberAuthor
scivision

scivision commented on Jan 9, 2020

@scivision
Member

to avoid namespace clashes, the constants could be in a derived type consisting mostly of parameter like

mc%pi  
mc%tau
certik

certik commented on Jan 9, 2020

@certik
MemberAuthor

We had that discussion at #49 about using derived types to workaround the insufficiency of Fortran's namespaces. I personally think it's not a good idea, you can read the arguments at #49.

But if the majority in the community wants to use a derived type for this, we can, especially while it is still in experimental. We can always revisit later.

The proper solution, in my opinion, is j3-fortran/fortran_proposals#1 and j3-fortran/fortran_proposals#86. If both are implemented, then one could do something like this:

use, namespace :: stdlib, only: constants
...
constants%pi

Where constants is the module nested in stdlib. This would be equivalent to Python's from stdlib import constants.

epagone

epagone commented on Jan 9, 2020

@epagone

I agree with @certik that the derived type solution is suboptimal, but I think it's the best that we can do with Fortran 2018. Once the standard is fixed (I wouldn't hold my breath meanwhile), we can revert to a better solution, similarly to what has been done with procedure optional argument default values and the optval function.

nncarlson

nncarlson commented on Jan 9, 2020

@nncarlson
Contributor

I think using a derived type for the constants is perfectly legitimate and if done "properly" acts exactly like a namespace and the user needn't even be aware that there is a derived type involved other than the "%" character which becomes part of the constant "name". For example,

module math_constants
  private
  type :: private_type
    real :: pi, e
  end type
  type(private_type), parameter, public :: mc = private_type(3.14, 2.71)
end module

use math_constants
print *, mc%pi, mc%e
end

Interestingly, for the NAG compiler this has 0 overhead compared to using individual parameters; the mc structure doesn't even appear in the C code for the print statement -- it's been completely unwound to the actual constants.

scivision

scivision commented on Jan 9, 2020

@scivision
Member

That's an interesting technique. Will have to check with other compiler disassemble to see if zero overhead holds there too.

certik

certik commented on Jan 9, 2020

@certik
MemberAuthor

I propose we use this:

module math_constants
  private
  real, parameter, public :: pi = 3.14, e_ = 2.71
end module

use math_constants, only: pi, e_
print *, pi, e_
end

Both the module and user code is shorter and simpler.

Using the derived type approach, how do I import just pi? I don't think you can. So all your user code will have to always type mc%pi.

Here is an example from one of my codes

VeeG = 4*pi*neG / G2

Much more readable than:

VeeG = 4*mc%pi*neG / G2
zjibben

zjibben commented on Jan 9, 2020

@zjibben

My personal preference is slightly toward @certik's bare parameters in a module, primarily for cleanliness in expressions. And very subjectively I realize, bundling constants into a single data object feels wrong. On the off chance there's a collision, one can always rename on use: use math_constants, only: mc_pi => pi. To me that's the superior compromise until true namespaces are available.

nncarlson

nncarlson commented on Jan 10, 2020

@nncarlson
Contributor

I want to clarify my earlier comment that I was not advocating for using the derived type approach in this case. I'm fairly ambivalent about it, and could go either way. I really just wanted to push back on the idea that this is misuse of derived types in general. I don't think it is. It provides a "namespace" like experience in lieu of having namespaces, and seems to do so without any overhead.

nshaffer

nshaffer commented on Jan 13, 2020

@nshaffer
Contributor

What precision should these constants be? Do we define, e.g., pi_sp, pi_dp, pi_qp and then expect the user to choose the one they want (giving them the chance to rename it)?

As for namespacing, I don't see the benefit here. Namespacing the constants seems like over-engineering, plus you lose the ability to just use pi or e_ as @certik mentioned.

epagone

epagone commented on Jan 13, 2020

@epagone

What precision should these constants be? Do we define, e.g., pi_sp, pi_dp, pi_qp and then expect the user to choose the one they want (giving them the chance to rename it)?

How about we follow a pragmatic approach and implement only the highest possible precision type ("qp"?) with no suffix and then let Fortran implicitly take care of the conversions? Is it too much a "quick and dirty" solution?

certik

certik commented on Jan 13, 2020

@certik
MemberAuthor

Regarding derived type versus just variables in a module: let's do both, so that we can all get what we want and move on. So let's do this:

module math_constants
private  
real, parameter, public :: pi = 3.14, e_ = 2.71  
 
type :: private_type  
    real :: pi = pi  
    real :: e = e_  
end type  
 
type(private_type), public, parameter :: mc = private_type()  
end module

Then this can be used both as:

use math_constants, only: pi, e_
print *, pi, e_
end

and as:

use math_constants, only: mc
print *, mc%pi, mc%e
end

I just tested it and it works.

There are essentially two camps here --- one side thinks it's an over engineering and an imperfect workaround for a fundamental deficiency of Fortran namespaces; the other side thinks it's worth using derived types as namespaces. The above approach gets both sides what they want, without forcing the other side to use an approach that feels wrong.

So let's try that and move on. We can do the same approach in #49.


Regarding the constant's precision, that's a very good point. If we set them to the highest precision available in the compiler, as in this code:

program test_pi
use stdlib_experimental_kinds, only: sp, dp, qp
implicit none
real(dp), parameter :: pi_dp    = 3.1415926535897932384626433832795_dp
real(qp), parameter :: pi_qp    = 3.1415926535897932384626433832795028841971_qp


real(dp) :: a
real(qp) :: b

a = pi_dp
print *, a
a = pi_qp
print *, a

b = pi_dp
print *, b
b = pi_qp
print *, b

end program

which prints:

   3.1415926535897931     
   3.1415926535897931     
   3.14159265358979311599796346854418516      
   3.14159265358979323846264338327950280      

Then it looks like things behave correctly. But unfortunately gfortran gives a warning:

test_pi.f90:13:4:

 a = pi_qp
    1
Warning: Change of value in conversion from ‘REAL(16)’ to ‘REAL(8)’ at (1) [-Wconversion]

Actually I do not even know how to get rid of this warning, as this also doesn't work:

test_pi.f90:13:9:

 a = real(pi_qp, dp)
         1
Warning: Change of value in conversion from ‘REAL(16)’ to ‘REAL(8)’ at (1) [-Wconversion]

So we need to figure this out.

27 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic: utilitiescontainers, strings, files, OS/environment integration, unit testing, assertions, logging, ...

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Implement constants module · Issue #99 · fortran-lang/stdlib