LCOV - code coverage report
Current view: top level - num/public - accurate_sum.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 97 0
Test Date: 2025-05-08 18:23:42 Functions: 0.0 % 22 0

            Line data    Source code
       1              : 
       2              : module accurate_sum
       3              : 
       4              :    use const_def, only: qp
       5              : 
       6              :    implicit none
       7              : 
       8              :    private
       9              :    public :: accurate_real, neumaier_sum, operator(+), operator(-), assignment(=), &
      10              :              operator(<), operator(>), operator(*), operator(/)
      11              : 
      12              :    ! Type for easily using Neumaier's summation algorithm in place of normal addition.
      13              :    type accurate_real
      14              :       real(qp) :: sum
      15              :       real(qp) :: compensator
      16              : 
      17              :    contains
      18              : 
      19              :       procedure :: value
      20              : 
      21              :    end type accurate_real
      22              : 
      23              :    interface operator(+)
      24              :       procedure add_accurate_real
      25              :       procedure add_accurate_real_num
      26              :       procedure add_accurate_real_num_rev
      27              :    end interface operator(+)
      28              : 
      29              :    interface operator(-)
      30              :       procedure sub_accurate_real
      31              :       procedure sub_accurate_real_num
      32              :       procedure sub_accurate_real_num_rev
      33              :    end interface operator(-)
      34              : 
      35              :    interface operator(*)
      36              :       procedure mult_acc_real
      37              :       procedure mult_real_acc
      38              :       procedure mult_acc_acc
      39              :    end interface operator(*)
      40              : 
      41              :    interface operator(/)
      42              :       procedure div_acc_real
      43              :       procedure div_real_acc
      44              :       procedure div_acc_acc
      45              :    end interface operator(/)
      46              : 
      47              :    interface assignment(=)
      48              :       procedure set_accurate_real_num
      49              :       procedure set_num_accurate_real
      50              :       procedure set_acc_acc
      51              :    end interface assignment(=)
      52              : 
      53              :    interface operator(<)
      54              :       procedure acc_less_than_num
      55              :       procedure num_less_than_acc
      56              :    end interface operator(<)
      57              : 
      58              :    interface operator(>)
      59              :       procedure acc_greater_than_num
      60              :       procedure num_greater_than_acc
      61              :    end interface operator(>)
      62              : 
      63              : contains
      64              : 
      65              :    ! Helper method for evaluating an accurate_real.
      66            0 :    function value(this) result(res)
      67              :       ! Inputs
      68              :       class(accurate_real), intent(in) :: this
      69              :       real(qp) :: res
      70              : 
      71            0 :       res = this%sum + this%compensator
      72            0 :    end function value
      73              : 
      74              :    ! Performs one step of Neumaier's summation algorithm
      75              :    ! (see doi:10.1002/zamm.19740540106)
      76              :    ! NOTE: Do not use --ffast-math or the like with this routine.
      77              :    ! The compiler will optimize away the trick which preserves
      78              :    ! accuracy.
      79            0 :    subroutine neumaier_sum(sum, compensator, summand)
      80              :       ! Inputs
      81              :       real(qp) :: sum, compensator, summand
      82              : 
      83              :       ! Intermediates
      84            0 :       real(qp) :: provisional
      85              : 
      86            0 :       provisional = sum + summand
      87            0 :       if (abs(sum) >= abs(summand)) then
      88            0 :          compensator = compensator + (sum - provisional) + summand
      89              :       else
      90            0 :          compensator = compensator + (summand - provisional) + sum
      91              :       end if
      92            0 :       sum = provisional
      93              : 
      94            0 :    end subroutine neumaier_sum
      95              : 
      96              :    ! The remaining are helper methods which overload +,-,*,/,=,<,>
      97              :    ! to work with accurate_real and real(qp) numbers interchangeably.
      98              : 
      99              :    ! *************
     100            0 :    type(accurate_real) function mult_acc_acc(op1, op2) result(ret)
     101              :       type(accurate_real), intent(in) :: op1
     102              :       type(accurate_real), intent(in) :: op2
     103              : 
     104            0 :       ret%sum = op1%sum*op2%sum
     105            0 :       ret%compensator = op1%compensator*op2%sum
     106              : 
     107            0 :       call neumaier_sum(ret%sum, ret%compensator, op1%sum*op2%compensator)
     108            0 :       call neumaier_sum(ret%sum, ret%compensator, op1%compensator*op2%compensator)
     109              : 
     110            0 :    end function mult_acc_acc
     111              : 
     112            0 :    type(accurate_real) function mult_acc_real(op1, op2) result(ret)
     113              :       type(accurate_real), intent(in) :: op1
     114              :       real(qp), intent(in) :: op2
     115              : 
     116            0 :       ret%sum = op1%sum*op2
     117            0 :       ret%compensator = op1%compensator*op2
     118              : 
     119            0 :    end function mult_acc_real
     120              : 
     121            0 :    type(accurate_real) function mult_real_acc(op1, op2) result(ret)
     122              :       real(qp), intent(in) :: op1
     123              :       type(accurate_real), intent(in) :: op2
     124              : 
     125            0 :       ret%sum = op2%sum*op1
     126            0 :       ret%compensator = op2%compensator*op1
     127              : 
     128            0 :    end function mult_real_acc
     129              : 
     130              :    ! ///////////
     131            0 :    type(accurate_real) function div_acc_acc(op1, op2) result(ret)
     132              :       type(accurate_real), intent(in) :: op1
     133              :       type(accurate_real), intent(in) :: op2
     134              : 
     135            0 :       ret%sum = op1%value()/op2%value()
     136            0 :       ret%compensator = 0
     137              : 
     138            0 :    end function div_acc_acc
     139              : 
     140            0 :    type(accurate_real) function div_acc_real(op1, op2) result(ret)
     141              :       type(accurate_real), intent(in) :: op1
     142              :       real(qp), intent(in) :: op2
     143              : 
     144            0 :       ret%sum = op1%sum/op2
     145            0 :       ret%compensator = op1%compensator/op2
     146              : 
     147            0 :    end function div_acc_real
     148              : 
     149            0 :    type(accurate_real) function div_real_acc(op1, op2) result(ret)
     150              :       real(qp), intent(in) :: op1
     151              :       type(accurate_real), intent(in) :: op2
     152              : 
     153            0 :       ret%sum = op1/op2%value()
     154            0 :       ret%compensator = 0
     155              : 
     156            0 :    end function div_real_acc
     157              : 
     158              :    ! ============
     159            0 :    subroutine set_accurate_real_num(this, new)
     160              :       type(accurate_real), intent(out) :: this
     161              :       real(qp), intent(in) :: new
     162            0 :       this%sum = new
     163            0 :       this%compensator = 0
     164            0 :    end subroutine set_accurate_real_num
     165              : 
     166            0 :    subroutine set_num_accurate_real(this, new)
     167              :       real(qp), intent(out) :: this
     168              :       type(accurate_real), intent(in) :: new
     169            0 :       this = new%value()
     170            0 :    end subroutine set_num_accurate_real
     171              : 
     172            0 :    subroutine set_acc_acc(this, new)
     173              :       type(accurate_real), intent(out) :: this
     174              :       type(accurate_real), intent(in) :: new
     175            0 :       this%sum = new%sum
     176            0 :       this%compensator = new%compensator
     177            0 :    end subroutine set_acc_acc
     178              : 
     179              :    ! <<<<<<<<<<<
     180            0 :    logical function acc_less_than_num(acc, num) result(ret)
     181              :       type(accurate_real), intent(in) :: acc
     182              :       real(qp), intent(in) :: num
     183            0 :       if (acc%value() < num) then
     184              :          ret = .true.
     185              :       else
     186            0 :          ret = .false.
     187              :       end if
     188            0 :    end function acc_less_than_num
     189              : 
     190            0 :    logical function num_less_than_acc(num, acc) result(ret)
     191              :       type(accurate_real), intent(in) :: acc
     192              :       real(qp), intent(in) :: num
     193            0 :       if (num < acc%value()) then
     194              :          ret = .true.
     195              :       else
     196            0 :          ret = .false.
     197              :       end if
     198            0 :    end function num_less_than_acc
     199              : 
     200              :    ! >>>>>>>>>>>
     201            0 :    logical function acc_greater_than_num(acc, num) result(ret)
     202              :       type(accurate_real), intent(in) :: acc
     203              :       real(qp), intent(in) :: num
     204            0 :       if (acc%value() > num) then
     205              :          ret = .true.
     206              :       else
     207            0 :          ret = .false.
     208              :       end if
     209            0 :    end function acc_greater_than_num
     210              : 
     211            0 :    logical function num_greater_than_acc(num, acc) result(ret)
     212              :       type(accurate_real), intent(in) :: acc
     213              :       real(qp), intent(in) :: num
     214            0 :       if (num > acc%value()) then
     215              :          ret = .true.
     216              :       else
     217            0 :          ret = .false.
     218              :       end if
     219            0 :    end function num_greater_than_acc
     220              : 
     221              :    ! +++++++++++
     222            0 :    type(accurate_real) function add_accurate_real(op1, op2) result(ret)
     223              :       type(accurate_real), intent(in) :: op1
     224              :       type(accurate_real), intent(in) :: op2
     225              : 
     226            0 :       ret%sum = op1%sum
     227            0 :       ret%compensator = op1%compensator
     228            0 :       call neumaier_sum(ret%sum, ret%compensator, op2%sum)
     229            0 :       call neumaier_sum(ret%sum, ret%compensator, op2%compensator)
     230              : 
     231            0 :    end function add_accurate_real
     232              : 
     233            0 :    type(accurate_real) function add_accurate_real_num(op1, op2) result(ret)
     234              :       type(accurate_real), intent(in) :: op1
     235              :       real(qp), intent(in) :: op2
     236              : 
     237            0 :       ret%sum = op1%sum
     238            0 :       ret%compensator = op1%compensator
     239            0 :       call neumaier_sum(ret%sum, ret%compensator, op2)
     240              : 
     241            0 :    end function add_accurate_real_num
     242              : 
     243            0 :    type(accurate_real) function add_accurate_real_num_rev(op1, op2) result(ret)
     244              :       real(qp), intent(in) :: op1
     245              :       type(accurate_real), intent(in) :: op2
     246              : 
     247            0 :       ret%sum = op2%sum
     248            0 :       ret%compensator = op2%compensator
     249            0 :       call neumaier_sum(ret%sum, ret%compensator, op1)
     250              : 
     251            0 :    end function add_accurate_real_num_rev
     252              : 
     253              :    ! -----------
     254            0 :    type(accurate_real) function sub_accurate_real(op1, op2) result(ret)
     255              :       type(accurate_real), intent(in) :: op1
     256              :       type(accurate_real), intent(in) :: op2
     257              : 
     258            0 :       ret%sum = op1%sum
     259            0 :       ret%compensator = op1%compensator
     260            0 :       call neumaier_sum(ret%sum, ret%compensator, -op2%sum)
     261            0 :       call neumaier_sum(ret%sum, ret%compensator, -op2%compensator)
     262              : 
     263            0 :    end function sub_accurate_real
     264              : 
     265            0 :    type(accurate_real) function sub_accurate_real_num(op1, op2) result(ret)
     266              :       type(accurate_real), intent(in) :: op1
     267              :       real(qp), intent(in) :: op2
     268              : 
     269            0 :       ret%sum = op1%sum
     270            0 :       ret%compensator = op1%compensator
     271            0 :       call neumaier_sum(ret%sum, ret%compensator, -op2)
     272              : 
     273            0 :    end function sub_accurate_real_num
     274              : 
     275            0 :    type(accurate_real) function sub_accurate_real_num_rev(op1, op2) result(ret)
     276              :       real(qp), intent(in) :: op1
     277              :       type(accurate_real), intent(in) :: op2
     278              : 
     279            0 :       ret%sum = op1
     280            0 :       ret%compensator = -op2%compensator
     281            0 :       call neumaier_sum(ret%sum, ret%compensator, -op2%sum)
     282              : 
     283            0 :    end function sub_accurate_real_num_rev
     284              : 
     285            0 : end module accurate_sum
        

Generated by: LCOV version 2.0-1