>> Rodney Roberts IS & Education Professional Homepage   >> Programming Tutorials And Downloads






Discover Engineering


Science makes it known,
Engineering makes it work,
Art makes it beautiful.


 

Using a simple D program to call Numerical Recipes: The Art of Scientific Computing1
Silverfrost Fortran 95 subroutines

This  page  discusses  developing  a  simple  Win32  Command Prompt  D  program  calling 
statistical analysis subroutines in a FORTRAN .dll (sttstcs.dll).   sttstcs.dll was developed on a PIII running
Win XP Pro SP2 using SilverFrost FTN95.  The D program (dstat.d) was developed on a HP P6 AMD Dual Core
running Win 7 Home Premium with Notepad++ and DMD32 D Compiler v2.066.0.

Why FORTRAN? Doing a Google search for ieee why Fortran scientific programming shows Fortran is widely
used in engineering and scientific programming.  Being able to link in FORTRAN libraries in the form of a .dll in other
programming languages combines the strengths of both.

Sample D Code

Below is a listing of the D main program (dstat.d) to call the various FORTRAN statistical analysis subroutines to
calculate Average, Median, Standard Deviation, Variance, etc., for a series of grades (referred to as data points). The
FORTRAN statistical analysis subroutines called are IMDIAN1(...), IMOMENT(...), and IMODE(...).

import std.stdio;

// sttstcs.dll subroutine declarations
extern (Pascal)
{
  void* IMDIAN1(ref short, ref short, ref short, ref short[300]);
  void* IMOMENT(ref short, ref float, ref float, ref float, ref float, ref float, ref float, ref short, ref short[300]);
  void* IMODE(ref short, ref short, ref short, ref short, ref short, ref short[300]);
}

int main(string[] args)
{
  short[300] idata;
  short   i, NumVal, NumUnqVal, iMed, intMode, modeCnt, iErr;
  float avg, aDev, sDev, varnc, skew, curt;

  printf("dstat - d Statistics Copyright 2014 Rodney Roberts\n");
  printf("Silverfrost FTN95 Numerical Recipes .dll demonstration\n\n");
  NumVal = 21;
  NumUnqVal=iMed = intMode = modeCnt = iErr = 0;
  avg = aDev = sDev = varnc = skew = curt = 0.0;
  idata[] = 0;
  idata[0] = 84;
  idata[1] = 62;
  idata[2] = 96;
  idata[3] = 68;
  idata[4] = 86;
  idata[5] = 72;
  idata[6] = 86;
  idata[7] = 75;
  idata[8] = 86;
  idata[9] = 75;
  idata[10] = 86;
  idata[11] = 78;
  idata[12] = 88;
  idata[13] = 82;
  idata[14] = 88;
  idata[15] = 83;
  idata[16] = 81;
  idata[17] = 83;
  idata[18] = 92;
  idata[19] = 94;
  idata[20] = 95;
  printf("Number of data points: %d\n\n", NumVal);
  printf("Data points: \n");
  IMDIAN1(iErr,iMed,NumVal,idata);
  if (iErr!=0)
  {
    printf("IMDIAN1 call error %d\n", iErr);
    return(iErr);
  }
  for (i = 0; i < NumVal; i++)
  {
    printf(" %d ", idata[i]);
    if (((i/5)*5)==i)
      printf("\n");
  }
  printf("\n");
  printf("Statistical Descriptions of Data\n");
  printf("--------------------------------\n");
  printf("  Median Value: %d\n", iMed);
  IMOMENT(iErr,curt,skew,varnc,sDev,aDev,avg,NumVal,idata);
  if (iErr!=0)
  {
    printf("IMOMENT call error %d\n", iErr);
    return(iErr);
  }
  printf("       Average: %f\n", avg);
  printf("Std. Deviation: %f\n", sDev);
  printf("Avg. Deviation: %f\n", aDev);
  printf("      Variance: %f\n", varnc);
  printf("          Skew: %f\n\n", skew);
  IMODE(iErr,NumUnqVal,modeCnt,intMode,NumVal,idata);
  if ((iErr!=0)&&(iErr!=2))
  {
    printf("IMODE call error %d\n", iErr);
    return(iErr);
  }
  printf("          Mode: %d\n", intMode);
  printf("    Mode Count: %d\n", modeCnt);
  if (iErr==2)
  {
    printf("Multiple modes\n");
  }
  return (0);
}


Static and Dynamic Arrays

FORTRAN arrays are dimensioned 1..size, Pascal arrays are dimensioned <low ordinal value>..<high ordinal value>,
COBOL arrays (tables) are dimensioned 1..size, and D arrays are dimensioned 0..size-1.  FORTRAN matrices are
dimensioned (rows, columns) (same as the mathematical definition),  D (and Pascal) matrices are dimensioned [columns][rows].  Therefore, FORTRAN's X(3,5) refers to the same element as D's x[4][2] (D treats it as an array of rows of column arrays, adding complexity;2 when processing matrices in a D multi-language program, it may be simpler to perform the bulk of matrix processing in FORTRAN and/or Pascal)  and Pascal's x[5,3] (if <low ordinal value> = 1).

As can be seen, used a static array for idata. On another project, tried to use a D dynamic array with the FORTRAN subroutine defining it as <type> <number of values>, <array name>(<number of values>).  In this example, it would be:
INTEGER*2 NumVal, idata(NumVal) 		     
       
matrix rows and columns
(the above is an example of dynamically dimensioned (or dimensioned with variable bounds) FORTRAN array)
Unfortunately, there is something in the D dynamic array structure which causes the array element start addresses not
to line up with the corresponding FORTRAN array elements.  However, it is possible to pass a D static array to a
FORTRAN array dimensioned with variable bounds as above.   FORTRAN allows passing individual subscripted array
elements by reference; see tir33.for Subprograms for an example.


When working with matrices, explicitly define a static size.

(FORTRAN's real, dimension(*) :: <array name> may work with D dynamic arrays but is currently outside the scope of
these tutorials and has not been tested.)



extern (...) {...}

An extern (...) {...} declaration with a linkage attribute is needed since IMDIAN1(...), IMOMENT(...), and IMODE(...) are
defined in another object file.  FORTRAN converts the identifiers in its symbol table to upper case, therefore the
subroutine names must be in upper case.

It may help to think of it as:
extern (<calling model>)
{<returned data type> <external procedure name> (<passed parameter data types list>)}
3

extern (Pascal)
{
  void* IMDIAN1(ref short, ref short, ref short, ref short[300]);
  void* IMOMENT(ref short, ref float, ref float, ref float, ref float, ref float, ref float, ref short, ref short[300]);
  void* IMODE(ref short, ref short, ref short, ref short, ref short, ref short[300]);
}
       
void* (or void) is used for FORTRAN subroutine and Pascal procedure calls, indicating the subprogram does not
return a value.

Using anything other than extern (Pascal) {...} to declare the external sttstcs.dll subroutines (which were originally
meant for the stdcall model) caused a variant of Error 42: Symbol Undefined IMDIAN.4   extern (Pascal) {...}
causes the passed arguments to be Pushed/Popped to/from the stack in a different order, requiring the parameters
passed to the external sttstcs.dll subroutines be listed in reverse order in the call statement.  By default, FORTRAN
requires its parameters to be passed by reference (can be overridden in the FORTRAN subroutine in a conventional
all FORTRAN application).  FORTRAN's parameter passing by reference requirement necessitated the ref keywords
in the extern (...) {...} declaration (void* IMDIAN1(ref short, ref short, ref short, ref short[300]), etc.).

Trying to avoid side effects by combining FORTRAN’s INTENT(IN):: with D’s in in the extern statement produced an object. Error@(0): Access Violation, though it is possible I may have missed something.  On another project, tried
leaving out the ref keyword in the extern (...) {...} declaration, which produced a throwable exception.



Compiling and Linking

First, prepare the sttstcs.lib import library using implib (download file bup.zip) (if you have not already done so); the SilverFrost generated sttstcs.lib is not compatible with the Digital Mars D compiler/linker.

Then compile and link dstat.d in a Command Prompt in a single step using the D command line compiler.

(type dmd --help > dmd.txt to print help to a text file)

(For interoperatibility with default SilverFrost Fortran 95 and Free Pascal, use the D2 32-bit Command Prompt)
Win7 - compiling/linking dstat.d

Execute dstat.exe in a Command Prompt.

Obviously, this is a simple program (like the infamous "Hello World" program).  dstat.exe passes the array to the three subroutines (which calculate average, etc.) and then displays the results.



(Screenshots taken from HP P6 AMD Dual Core, Win 7 Home Premium, Command Prompt)
Win7 - dstat.exe output

Known Problems D Calling FORTRAN

  1. eX functions [EXP( ), SINH( ), etc.] in FORTRAN subroutines
  2. FORTRAN subroutines converting REAL data to INTEGER data
    (with or without the FLOOR( ) or INT( ) functions)
  3. returning COMPLEX variables from a Silverfrost FORTRAN COMPLEX FUNCTION
  4. Dynamic Arrays, mentioned above
All conditions execute fine in an all FORTRAN solution (the first item works in a Pascal/FORTRAN solution,
the second item works in a COBOL / FORTRAN solution).

However, when a D program calls a FORTRAN subroutine doing either of the first two conditions, it crashes with an
object.Error@(0): Invalid Floating Point Operation5

This reduces the versatility of D.





1. Press, William H., Brian P. Flannery, Saul A Teukolsky, and William T. Vetterling (1986). Numerical
Recipes: The Art of Scientific Computing
. New York:Press Syndicate of the University of Cambridge.
2. In one instance, even though the matrix was dimensioned [columns][rows], it was behaving
as if it was dimensioned [rows][columns].  Was able to satisfactorily process the matrix by passing
it to Object Pascal procedures.
3. See Variable Storage Compatibility and Equivalency for D / FORTRAN data type cross-reference.
4. Using implib's /system switch when generating sttstcs.lib may resolve this issue.
5. Have ran into Floating Point errors/problems in D calling Pascal and Pascal calling D projects:
a. In a main D Windows module calling an Object Pascal procedure reading a file, converting float data
to a string and then outputting using   SendMessageA(...,WM_SETTEXT,...,...)
immediately prior to calling Pascal procedure to read a file, caused file error 6 (Invalid file handle)
to be reported (moving code around resolved issue, could have been a Win32 API problem as well);
b. In an Object Pascal main Windows program (missle05.pas) calling a D function declared extern (C),
it was necessary to call   _control87(_MCW_EM, _MCW_EM)   to prevent an
Invalid-floating-point-operation in the D function (which did not perform any floating operations).


Any and all © copyrights, ™ trademarks, or other intellectual property (IP) mentioned here are the property of their respective owners.  No infringement is intended.

Feel free to use any of the above in your project; please give credit (same idea as Copyleft).  The source code files available for download on these pages are enhanced (including error fixes) and tested periodically - check often for revised versions (some portions of source code still in beta).

Page best viewed with Mozilla FireFox 3.6.13 (or higher), and Safari 5.1.7.

Website is supported on DESKTOP platforms only.

Web hosting provided by  
Award Space Web Hosting ,   Free Web Hosting. , & Gigarank/GigaRocket Web Hosting



>> Rodney Roberts IS & Education Professional Homepage   >> Programming Tutorials And Downloads