Precision and polynomial estimation in Fortran and Python

I'll provide a short description here and context below. I evaluate 2D polynomials (polynomials in 2 variables) in both Python and Fortran and getting different results. The relative error for my test - 4.23e-3 - is large enough not to be obvious due to differences in accuracy. The following code snippets use pretty primitive types and the same algorithm to try and make things as comparable as possible. Any clues regarding the discrepancy? I have tried changing the precision (in both Fortran c selected_real_kind

and Python c numpy.float128

), Fortran compilation (specifically the optimization level), and the algorithm (Horner's method numpy

). Any clues regarding the discrepancy? Errors in any version of the code? I saw the exact mismatch between Fortran and Python (sin function)but he hasn't had the opportunity to fully test it with different compilers.


Python:

#!/usr/bin/env python
""" polytest.py
Test calculation of a bivariate polynomial.
"""

# Define polynomial coefficients
coeffs = (
    (101.34274313967416, 100015.695367145, -2544.5765420363),
    (5.9057834791235253,-270.983805184062,1455.0364540468),
    (-12357.785933039,1455.0364540468,-756.558385769359),
    (736.741204151612,-672.50778314507,499.360390819152)
)
nx = len(coeffs)
ny = len(coeffs[0])

# Values of variables
x0 = 0.0002500000000011937
y0 = -0.0010071334522899211

# Calculate polynomial by looping over powers of x, y
z = 0.
xj = 1.
for j in range(nx):
    yk = 1.
    for k in range(ny):
        curr = coeffs[j][k] * xj * yk
        z += curr
        yk *= y0
    xj *= x0

print(z)   # 0.611782174444

      


Fortran:

! polytest.F90
! Test calculation of a bivariate polynomial.

program main

  implicit none
  integer, parameter :: dbl = kind(1.d0)
  integer, parameter :: nx = 3, ny = 2
  real(dbl), parameter :: x0 = 0.0002500000000011937, &
                          y0 = -0.0010071334522899211
  real(dbl), dimension(0:nx,0:ny) :: coeffs
  real(dbl) :: z, xj, yk, curr
  integer :: j, k

  ! Define polynomial coefficients
  coeffs(0,0) = 101.34274313967416d0
  coeffs(0,1) = 100015.695367145d0
  coeffs(0,2) = -2544.5765420363d0
  coeffs(1,0) = 5.9057834791235253d0
  coeffs(1,1) = -270.983805184062d0
  coeffs(1,2) = 1455.0364540468d0
  coeffs(2,0) = -12357.785933039d0
  coeffs(2,1) = 1455.0364540468d0
  coeffs(2,2) = -756.558385769359d0
  coeffs(3,0) = 736.741204151612d0
  coeffs(3,1) = -672.50778314507d0
  coeffs(3,2) = 499.360390819152d0

  ! Calculate polynomial by looping over powers of x, y
  z = 0d0
  xj = 1d0
  do j = 0, nx-1
    yk = 1d0
    do k = 0, ny-1
      curr = coeffs(j,k) * xj * yk
      z = z + curr
      yk = yk * y0
    enddo
    xj = xj * x0
  enddo

  ! Print result
  WRITE(*,*) z   ! 0.61436839888538231

end program

      

Compiled with: gfortran -O0 -o polytest.o polytest.F90


Context: I'm writing a pure Python implementation of an existing Fortran library, primarily as an exercise, but to add some flexibility. I compare my results with Fortran and get pretty much everything in the 1-10 range, but that's not in my hands. Other functions are also much more complex, which leads to inconsistency of simple polynomials.

Specific coefficients and test variables come from this library. The actual polynomial actually has degree (7.6) in (x, y), so there aren't many coefficients here. The algorithm is taken directly from Fortran, so if this is wrong I should contact the original developers. Generic functions can also compute derivatives, which is part of why this implementation might not be optimal - I know I still need to write a version of Horner's method, but that hasn't changed the discrepancy. I only noticed these errors when calculating derivatives at large y values, but the error persists in this simpler setting.

+3


source to share


1 answer


Two things in the Fortran code need to be adjusted to get consistent results between the Python and Fortran versions.

1. As you already did, declare a concrete double precision view as:

integer, parameter :: dbl = kind(0.d0)

      

Then you must define the variable by adding a view notation like:

real(dbl) :: z
z = 1.0_dbl

      



This is discussed, for example, at fortran90.org gotchas . The syntax might be awkward, but hey, I don't make rules.

2. Iteration over Fortran formatting is controlled by nx

and ny

. You intend to access every element coeffs

, but your indexing reduces the length of the iteration. Change nx-1

both ny-1

to nx

and ny

accordingly. Better yet, using inline Fortran ubound

to figure out the size along the desired size, for example:

do j = 0, ubound(coeffs, dim=1)

      

The updated code shown below fixes these issues and prints a result that matches the output generated by your Python code.

program main
    implicit none
    integer, parameter :: dbl = kind(1.d0)
    integer, parameter :: nx = 3, ny = 2
    real(dbl), parameter :: x0 = 0.0002500000000011937_dbl, &
                          y0 = -0.0010071334522899211_dbl
    real(dbl), dimension(0:nx,0:ny) :: coeffs
    real(dbl) :: z, xj, yk, curr
    integer :: j, k

    ! Define polynomial coefficients
    coeffs(0,0) = 101.34274313967416_dbl
    coeffs(0,1) = 100015.695367145_dbl
    coeffs(0,2) = -2544.5765420363_dbl
    coeffs(1,0) = 5.9057834791235253_dbl
    coeffs(1,1) = -270.983805184062_dbl
    coeffs(1,2) = 1455.0364540468_dbl
    coeffs(2,0) = -12357.785933039_dbl
    coeffs(2,1) = 1455.0364540468_dbl
    coeffs(2,2) = -756.558385769359_dbl
    coeffs(3,0) = 736.741204151612_dbl
    coeffs(3,1) = -672.50778314507_dbl
    coeffs(3,2) = 499.360390819152_dbl

    ! Calculate polynomial by looping over powers of x, y
    z = 0.0_dbl
    xj = 1.0_dbl
    do j = 0, ubound(coeffs, dim=1)
        yk = 1.0_dbl
        do k = 0, ubound(coeffs, dim=2)
            print "(a,i0,a,i0,a)", "COEFF(",j,",",k,")="
            print *, coeffs(j,k)
            curr = coeffs(j,k) * xj * yk
            z = z + curr
            yk = yk * y0
        enddo
        xj = xj * x0
    enddo

    ! Print result
    WRITE(*,*) z   ! Result: 0.611782174443735
end program  

      

+3


source







All Articles