! EXPERIMENTAL MODULES FOR ARITHMETIC WITH ERROR BOUNDS - V0.2
!
! Contents:
!
! NUM_CONSTS Module defining the PRECISE realkind,
! some useful constants and routines
! ERR_ARITHMETIC Module implementing error analysis
! test An example program
!
! -----------------------------------------------------------------
!
! Author: Abraham Agay (agay@vms.huji.ac.il)
! Date: May 1999, revised on June 2001
!
! Disclaimer: The following software may contain bugs!
!
! Limitations:
!
! 1) Only the most important overloadings were implemented,
! e.g. operations involving integers or reals other than
! the PRECISE type were not, also some intrinsic functions.
!
! 2) The routines in this package doesn't check input vars,
! so bad input, e.g. negative error bounds will produce
! ridiculous results.
!
! 3) No attempt was made to code for efficiency, and speed
! was willingly sacrificed for clarity/elegance.
!
! -----------------------------------------------------------------
!
! Usage:
!
! 0) Add "use num_consts"in the main program,
! and call"num_check()"to verify you have PRECISE REALs.
! 1) Add "use err_arithmetic"in every compilation unit
! that does computations with REALs.
! 2) Replace all REAL declarations by "type(err_real) :: ".
! 3) Convert integers to"real(kind = PRECISE)"in all mixed
! REAL/INTEGER operations, e.g N --> real(N, PRECISE).
! 4) replace all I/O statements involving REALs with
! "write_err" and "read_err", etc.
! 5) Provide input with error bounds.
!
! -----------------------------------------------------------------
!
! Remarks:
!
! A strange feature of DEC Fortran 90: PARAMETERs can't be SAVEd!
! According to the Fortran 90 Handbook this is a non-standard feature.
! Does it matter when using module sub-programs?
!
! Another problem of DEC Fortran 90: too many optionalprocedure
! arguments break the code.
!
MODULE NUM_CONSTS implicitnone
! The following module selects the kind of real used according
! to specified precision and range parameters:
!
! SIG_DIGITS Number of significant digits required
! EXP_RANGE Exponent range required
!
! Various useful constants are then defined in the selected
! realtype, and two useful procedures.
real(kind=PRECISE),parameter :: &
ZERO = 0.0_PRECISE, &
ONE = 1.0_PRECISE, &
TWO = 2.0_PRECISE, &
THREE = 3.0_PRECISE, &
HALF = ONE / TWO ! 0.5_PRECISE
real(kind=PRECISE),parameter :: &
PI = 3.141592653589793238462643_PRECISE, &
TWOPI = PI * TWO, &
HALFPI = PI / TWO, &
DEG2RAD = PI / 180.0_PRECISE, &
RAD2DEG = 180.0_PRECISE / PI, &
RAD2MIN = 10800.0_PRECISE / PI, &
RAD2SEC = 648000.0_PRECISE / PI
contains
SUBROUTINE NUM_CHECK() selectcase (PRECISE) case (-1) stop'num_check: Insufficient precision ' case (-2) stop'num_check: Insufficient exponent range ' case (-3) stop'num_check: Insufficient precision and exponent range ' case default write(*,*) 'num_check: suitable type of REAL is available '
! call num_status() endselect return ENDSUBROUTINE NUM_CHECK
SUBROUTINE NUM_STATUS() real(kind=PRECISE) :: x
write (unit=*, fmt='(1x,a,i12)') & 'num_status: Base of number model is: ', radix(x) write (unit=*, fmt='(1x,a,i12)') & 'num_status: Number of digits in this base: ', digits(x) write (unit=*, fmt='(1x,a,i12)') & 'num_status: Minimum exponent in this base: ', minexponent(x) write (unit=*, fmt='(1x,a,i12)') & 'num_status: Maximum exponent in this base: ', maxexponent(x) write (unit=*, fmt='(1x,a,i12)') & 'num_status: The PRECISE real type is kind: ', kind(x) write (unit=*, fmt='(1x,a,i12)') & 'num_status: Decimal exponent range is: ', range(x) write (unit=*, fmt='(1x,a,es12.3)') & 'num_status: Smallest positive number is: ', tiny(x) write (unit=*, fmt='(1x,a,es12.3)') & 'num_status: Largest positive number is: ', huge(x) write (unit=*, fmt='(1x,a,i12,a)') & 'num_status: Decimal precision is: ', precision(x), ' digits ' write (unit=*, fmt='(1x,a,es12.3)') & 'num_status: Epsilon is: ', epsilon(x) write (unit=*, fmt='(1x)') ENDSUBROUTINE NUM_STATUS
ENDMODULE NUM_CONSTS
MODULE ERR_ARITHMETIC use NUM_CONSTS implicitnone PRIVATE
! We define 3 new compound types of real numbers: intervals,
! absolute error-ranges, and relative error-ranges.
!
! IVL_REAL (a, b)
!
! ERR_REAL (A +|- a) = (A - a, A + a)
!
! REL_REAL (A +|- a/A)
! Err-arithmetic is performed by:
!
! 1. Converting to intervals, implicitly or explicitly,
! 2. Performing the required operation
! 3. Rounding outwards
! 4. Converting back to err-arithmetic
! Arithmetical operations near a singularity, for example,
! when taking tan() near the point PI / TWO, give rise to
! multiply-connected error ranges. We can define, for each
! realtype two sub-types:
!
! I. Normal, contiguous, one-part interval
! IVL_REAL: LOWER <= UPPER
! ERR_REAL: ABSERR >= ZERO
!
! II. A two-part interval which is the set-theoretic
! complement of a type I interval.
! IVL_REAL: LOWER > UPPER
! ERR_REAL: ABSERR < ZERO
!
! However, this doesn't close interval operations, as more
! complicated error ranges may be produced. We will use
! only TYPE I intervals in this package, and flag an error
! when a singularity is encountered. The following two
! parameters control error handling:
! Extending assignment to convert between the 2 new types
! of reals and between the new types and the old ones.
! This technique will be used extensively, it's probably
! bad programming (see Cooper Redwine) but too tempting.
!
! Conversion toreal is not supported on purpose.
! INTERFACE SINH
! MODULEPROCEDURE ERR_SINH
! ENDINTERFACE
public :: read_err, write_err, &
write_txt, write_nl, &
write_txt_nl, write_one
contains
! These important checking routines are not used yet.
SUBROUTINE ERR_CHECK_ERR(A) type(err_real) :: a
if (a%abserr < ZERO) & call err_error(ERROR, 'err_check_err: bad error range ') return ENDSUBROUTINE ERR_CHECK_ERR
SUBROUTINE ERR_CHECK_IVL(A) type(ivl_real) :: a
if (a%lower > a%upper) & call err_error(ERROR, 'err_check_ivl: bad error range ') return ENDSUBROUTINE ERR_CHECK_IVL
! A useful routine, rounds an interval outwards.
!
! Using the "spacing"function instead of "nearest" is
! probably incorrect.
!
! err_round%lower = a%lower - spacing(a%lower)
! err_round%upper = a%upper + spacing(a%upper)
!
! One example that comes to mind: with DEC floating-point
! numbers, the spacing is actually asymmetrical (?).
FUNCTION ERR_ROUND(A) type(ivl_real) :: err_round, a
SUBROUTINE IVL_2_ERR(A, B) type(err_real) :: a type(ivl_real), intent(in) :: b
a%number = (b%upper + b%lower) / TWO
a%abserr = (b%upper - b%lower) / TWO return ENDSUBROUTINE IVL_2_ERR
SUBROUTINE REAL_2_ERR(A, B) type(err_real) :: a real(kind = PRECISE), intent(in) :: b
a%number = b
a%abserr = ZERO return ENDSUBROUTINE REAL_2_ERR
SUBROUTINE REAL_2_IVL(A, B) type(ivl_real) :: a real(kind = PRECISE), intent(in) :: b
a%lower = b
a%upper = b return ENDSUBROUTINE REAL_2_IVL
! D e f i n i n g i n t e r v a l i n c l u s i o n (new operator)
FUNCTION ERR_IN_IVL_IVL(A, B) logical :: err_in_ivl_ivl type(ivl_real), intent(in) :: a, b
if ((a%lower >= b%lower) .and. (a%upper <= b%upper)) then
err_in_ivl_ivl = .true. else
err_in_ivl_ivl = .false. endif return ENDFUNCTION ERR_IN_IVL_IVL
FUNCTION ERR_IN_REAL_IVL(A, B) logical :: err_in_real_ivl real(kind = PRECISE), intent(in) :: a type(ivl_real), intent(in) :: b type(ivl_real) :: tmp
tmp = a
err_in_real_ivl = tmp .in. b return ENDFUNCTION ERR_IN_REAL_IVL
! E x t e n d i n g r e l a t i o n a l o p e r a t o r s
! .gt.
FUNCTION ERR_GT_ERR_ERR(A, B) logical :: err_gt_err_err type(err_real), intent(in) :: a, b type(ivl_real) :: x, y
x = a
y = b if (x%lower > y%upper) then
err_gt_err_err = .true. else
err_gt_err_err = .false. endif return ENDFUNCTION ERR_GT_ERR_ERR
FUNCTION ERR_GT_REAL_ERR(A, B) logical :: err_gt_real_err real(kind = PRECISE), intent(in) :: a type(err_real), intent(in) :: b type(err_real) :: tmp
tmp = a
err_gt_real_err = (tmp .gt. b) return ENDFUNCTION ERR_GT_REAL_ERR
FUNCTION ERR_GT_ERR_REAL(A, B) logical :: err_gt_err_real type(err_real), intent(in) :: a type(err_real) :: tmp real(kind = PRECISE), intent(in) :: b
tmp = b
err_gt_err_real = (a .gt. tmp) return ENDFUNCTION ERR_GT_ERR_REAL
! .ge.
FUNCTION ERR_GE_ERR_ERR(A, B) logical :: err_ge_err_err type(err_real), intent(in) :: a, b type(ivl_real) :: x, y
x = a
y = b if (x%lower >= y%upper) then
err_ge_err_err = .true. else
err_ge_err_err = .false. endif return ENDFUNCTION ERR_GE_ERR_ERR
FUNCTION ERR_GE_REAL_ERR(A, B) logical :: err_ge_real_err real(kind = PRECISE), intent(in) :: a type(err_real), intent(in) :: b type(err_real) :: tmp
tmp = a
err_ge_real_err = (tmp .ge. b) return ENDFUNCTION ERR_GE_REAL_ERR
FUNCTION ERR_GE_ERR_REAL(A, B) logical :: err_ge_err_real type(err_real), intent(in) :: a type(err_real) :: tmp real(kind = PRECISE), intent(in) :: b
tmp = b
err_ge_err_real = (a .ge. tmp) return ENDFUNCTION ERR_GE_ERR_REAL
! .lt.
FUNCTION ERR_LT_ERR_ERR(A, B) logical :: err_lt_err_err type(err_real), intent(in) :: a, b type(ivl_real) :: x, y
x = a
y = b if (x%lower < y%upper) then
err_lt_err_err = .true. else
err_lt_err_err = .false. endif return ENDFUNCTION ERR_LT_ERR_ERR
FUNCTION ERR_LT_REAL_ERR(A, B) logical :: err_lt_real_err real(kind = PRECISE), intent(in) :: a type(err_real), intent(in) :: b type(err_real) :: tmp
tmp = a
err_lt_real_err = (tmp .lt. b) return ENDFUNCTION ERR_LT_REAL_ERR
FUNCTION ERR_LT_ERR_REAL(A, B) logical :: err_lt_err_real type(err_real), intent(in) :: a type(err_real) :: tmp real(kind = PRECISE), intent(in) :: b
tmp = b
err_lt_err_real = (a .lt. tmp) return ENDFUNCTION ERR_LT_ERR_REAL
! .le.
FUNCTION ERR_LE_ERR_ERR(A, B) logical :: err_le_err_err type(err_real), intent(in) :: a, b type(ivl_real) :: x, y
x = a
y = b if (x%lower <= y%upper) then
err_le_err_err = .true. else
err_le_err_err = .false. endif return ENDFUNCTION ERR_LE_ERR_ERR
FUNCTION ERR_LE_REAL_ERR(A, B) logical :: err_le_real_err real(kind = PRECISE), intent(in) :: a type(err_real), intent(in) :: b type(err_real) :: tmp
tmp = a
err_le_real_err = (tmp .le. b) return ENDFUNCTION ERR_LE_REAL_ERR
FUNCTION ERR_LE_ERR_REAL(A, B) logical :: err_le_err_real type(err_real), intent(in) :: a type(err_real) :: tmp real(kind = PRECISE), intent(in) :: b
tmp = b
err_le_err_real = (a .le. tmp) return ENDFUNCTION ERR_LE_ERR_REAL
! E x t e n d i n g a d d i t i o n
! Master addition definition, if one operand is real we convert.
!
! Why not use just:
!
! err_add_err_err%number = a%number + b%number
! err_add_err_err%abserr = a%abserr + b%abserr
!
! The idea is to have the same procedure of rounding as in the
! multiplication and division procedures. In both cases we go
! via interval arithmetic, and perform there the rounding.
!
! Another alternative is:
!
! tmp%lower = a%number + b%number - a%abserr - b%abserr
! tmp%upper = a%number + b%number + a%abserr + b%abserr
! err_add_err_err = err_round(tmp)
!
! This is o.k., but we will go for a more elegant method.
FUNCTION ERR_ADD_ERR_ERR(A,B) type(err_real) :: err_add_err_err type(err_real), intent(in) :: a, b type(ivl_real) :: x, y, tmp
x = a
y = b
tmp%lower = x%lower + y%lower
tmp%upper = x%upper + y%upper
err_add_err_err = err_round(tmp) return ENDFUNCTION ERR_ADD_ERR_ERR
FUNCTION ERR_ADD_REAL_ERR(A,B) type(err_real) :: err_add_real_err, tmp real(kind = PRECISE), intent(in) :: a type(err_real), intent(in) :: b
tmp = a
err_add_real_err = tmp + b return ENDFUNCTION ERR_ADD_REAL_ERR
FUNCTION ERR_ADD_ERR_REAL(A,B) type(err_real) :: err_add_err_real, tmp type(err_real), intent(in) :: a real(kind = PRECISE), intent(in) :: b
tmp = b
err_add_err_real = a + tmp return ENDFUNCTION ERR_ADD_ERR_REAL
! E x t e n d i n g s u b s t r a c t i o n
! Master substraction definition, if one operand is real we convert.
!
! err_sub_err_err%number = a%number - b%number
! err_sub_err_err%abserr = a%abserr + b%abserr
!
! Again, we don't use these formulae, as we want to get a standard
! method of rounding.
!
! Another method:
!
! tmp%lower = a%number - b%number - a%abserr - b%abserr
! tmp%upper = a%number - b%number + a%abserr + b%abserr
! err_sub_err_err = err_round(tmp)
!
! This is o.k., but we will go for a more elegant method,
! via the operation of unary minus and addition.
FUNCTION ERR_SUB_UNARY_ERR(A) type(err_real) :: err_sub_unary_err type(err_real), intent(in) :: a
FUNCTION ERR_MULT_REAL_ERR(A,B) type(err_real) :: err_mult_real_err, tmp real(kind = PRECISE), intent(in) :: a type(err_real), intent(in) :: b
tmp = a
err_mult_real_err = tmp + b return ENDFUNCTION ERR_MULT_REAL_ERR
FUNCTION ERR_MULT_ERR_REAL(A,B) type(err_real) :: err_mult_err_real, tmp type(err_real), intent(in) :: a real(kind = PRECISE), intent(in) :: b
tmp = b
err_mult_err_real = a + tmp return ENDFUNCTION ERR_MULT_ERR_REAL
! E x t e n d i n g d i v i s i o n
FUNCTION ERR_DIV_ERR_ERR(A,B) type(err_real) :: err_div_err_err type(err_real), intent(in) :: a, b type(ivl_real) :: x, y, tmp
x = a
y = b if (ZERO .in. y) then
err_div_err_err = err_real(ZERO, huge(ZERO)) ! Stupid handling? call err_error(WARN, 'err_div_err_err: infinite error range ') else
tmp%lower = min(x%lower / y%lower, x%lower / y%upper, &
x%upper / y%lower, x%upper / y%upper)
tmp%upper = max(x%lower / y%lower, x%lower / y%upper, &
x%upper / y%lower, x%upper / y%upper)
err_div_err_err = err_round(tmp) endif return ENDFUNCTION ERR_DIV_ERR_ERR
FUNCTION ERR_DIV_REAL_ERR(A,B) type(err_real) :: err_div_real_err, tmp real(kind = PRECISE), intent(in) :: a type(err_real), intent(in) :: b
tmp = a
err_div_real_err = tmp / b return ENDFUNCTION ERR_DIV_REAL_ERR
FUNCTION ERR_DIV_ERR_REAL(A,B) type(err_real) :: err_div_err_real, tmp type(err_real), intent(in) :: a real(kind = PRECISE), intent(in) :: b
tmp = b
err_div_err_real = a / tmp return ENDFUNCTION ERR_DIV_ERR_REAL
! E x t e n d i n g e l e m e n t a l f u n c t i o n s
! The ABS intrinsic doesn't need an outward rounding.
FUNCTION ERR_ABS(A) type(err_real) :: err_abs type(err_real), intent(in) :: a type(ivl_real) :: x, tmp
x = a
tmp%upper = max(abs(x%lower), abs(x%upper))
tmp%lower = min(abs(x%lower), abs(x%upper)) ! Should be in"else" if (ZERO .in. x) then
err_abs = ivl_real(ZERO, tmp%upper) else
err_abs = tmp endif return ENDFUNCTION ERR_ABS
! V a r i o u s p o w e r o p e r a t i o n s
! How to treat a sqrt() of a range which is partly negative?
FUNCTION ERR_SQRT(A) type(err_real) :: err_sqrt type(err_real), intent(in) :: a type(ivl_real) :: x, tmp
x = a
tmp%lower = sqrt(max(x%lower, ZERO)) ! o.k. ???
tmp%upper = sqrt(x%upper)
err_sqrt = err_round(tmp) return ENDFUNCTION ERR_SQRT
FUNCTION ERR_EXP(A) type(err_real) :: err_exp type(err_real), intent(in) :: a type(ivl_real) :: x, tmp
x = a
tmp%lower = exp(x%lower)
tmp%upper = exp(x%upper)
err_exp = err_round(tmp) return ENDFUNCTION ERR_EXP
FUNCTION ERR_LOG(A) type(err_real) :: err_log type(err_real), intent(in) :: a type(ivl_real) :: x, tmp
x = a
tmp%lower = log(x%lower)
tmp%upper = log(x%upper)
err_log = err_round(tmp) return ENDFUNCTION ERR_LOG
! Defining the exponentiation operator.
! The integercase is not treated as a privatecase of real,
! to allow the compiler todo its accuracy optimizations
FUNCTION ERR_POWER_ERR_INT(A,B) type(err_real) :: err_power_err_int type(err_real), intent(in) :: a integer, intent(in) :: b type(ivl_real) :: x, tmp
x = a
tmp%lower = min(x%lower ** b, x%upper ** b)
tmp%upper = max(x%lower ** b, x%upper ** b)
err_power_err_int = err_round(tmp) return ENDFUNCTION ERR_POWER_ERR_INT
FUNCTION ERR_POWER_ERR_REAL(A,B) type(err_real) :: err_power_err_real type(err_real), intent(in) :: a real(kind = PRECISE), intent(in) :: b type(ivl_real) :: x, tmp
x = a
tmp%lower = min(x%lower ** b, x%upper ** b)
tmp%upper = max(x%lower ** b, x%upper ** b)
err_power_err_real = err_round(tmp) return ENDFUNCTION ERR_POWER_ERR_REAL
! E x t e n d i n g t r i g o n o m e t r i c f u n c t i o n s
! SIN is our primitive. We will reduce COS and TAN to SIN.
! There is an alternative primitive implementation of TAN,
! but it is not used.
FUNCTION ERR_SIN(A) type(err_real) :: err_sin type(err_real), intent(in) :: a type(ivl_real) :: x, tmp real(kind = PRECISE) :: offset
if (a%abserr >= TWOPI) then
err_sin = err_real(ZERO, ONE) else
x = a
offset = x%lower - mod(x%lower, TWOPI) if ((offset + HALFPI) .in. x) then
tmp%upper = ONE if ((offset + THREE * HALFPI) .in. x) then
tmp%lower = - ONE else
tmp%lower = min(sin(x%lower), sin(x%upper)) endif else
tmp%upper = max(sin(x%lower), sin(x%upper)) if ((offset + THREE * HALFPI) .in. x) then
tmp%lower = - ONE else
tmp%lower = min(sin(x%lower), sin(x%upper)) endif endif
err_sin = err_round(tmp) endif return ENDFUNCTION ERR_SIN
FUNCTION ERR_COS(A) type(err_real) :: err_cos type(err_real), intent(in) :: a
! This direct algorithm seems to give identical results.
FUNCTION ERR_ALT_TAN(A) type(err_real) :: err_alt_tan type(err_real), intent(in) :: a type(ivl_real) :: x, tmp real(kind = PRECISE) :: offset
if (a%abserr >= TWOPI) then
err_alt_tan = err_real(ZERO, huge(ZERO)) call err_error(WARN, 'err_alt_tan: a singularity ') else
x = a
offset = x%lower - mod(x%lower, PI) if ((offset + HALFPI) .in. x) then
! err_alt_tan = ivl_real(tan(x%lower), tan(x%upper)) ! Reversed range
err_alt_tan = err_real(ZERO, huge(ZERO)) call err_error(WARN, 'err_alt_tan: a singularity ') else
tmp%lower = min(tan(x%lower), tan(x%upper))
tmp%upper = max(tan(x%lower), tan(x%upper))
err_alt_tan = err_round(tmp) endif endif return ENDFUNCTION ERR_ALT_TAN
! ASIN is monotonic increasing, and hence easy to implement.
FUNCTION ERR_ASIN(A) type(err_real) :: err_asin type(err_real), intent(in) :: a type(ivl_real) :: x, tmp
x = a
tmp%lower = asin(x%lower)
tmp%upper = asin(x%upper)
err_asin = err_round(tmp) ENDFUNCTION ERR_ASIN
! We use the identity: asin(z) + acos(z) = HALFPI
FUNCTION ERR_ACOS(A) type(err_real) :: err_acos type(err_real), intent(in) :: a
err_acos = HALFPI - asin(a) ENDFUNCTION ERR_ACOS
! ATAN is monotonic increasing on the whole real line.
FUNCTION ERR_ATAN(A) type(err_real) :: err_atan type(err_real), intent(in) :: a type(ivl_real) :: x, tmp
x = a
tmp%lower = atan(x%lower)
tmp%upper = atan(x%upper)
err_atan = err_round(tmp) ENDFUNCTION ERR_ATAN
! We are trying to introduce here a new convention:
!
! NameTypeFormat
! ========= ======== ======
! Intervals IVL_REAL (x,y)
! Abs error ERR_REAL <x,y>
! Rel error REL_REAL [x,y]
! Reading one err-real without advancing
SUBROUTINE READ_ERR(LUN, A, PROMPT) integer :: lun, l, l1, l2, i, j type(err_real) :: a character(len=*), optional, volatile :: prompt character(len=80) :: buf1, buf2 character(len=1) :: c, begin, end
if (present(prompt)) & write(unit=*, fmt='(1x,a)', advance='NO') prompt
buf1 = ' ' read(unit=lun, fmt='(a)') buf1
buf2 = ' '
l = len_trim(buf1)
j = 1 do i = 1, l
c = buf1(i:i) if (c .ne. ' ') then
buf2(j:j) = c
j = j + 1 endif enddo
begin = buf2(1:1) if (begin .ne. '<') & call err_error(ERROR, 'err_read: number should begin with "<"')
l1 = index(buf2, ',') if (l1 .eq. 0) & call err_error(ERROR, 'err_read: no comma separator found')
l2 = len_trim(buf2) end = buf2(l2:l2) if (end .ne. '>') & call err_error(ERROR, 'err_read: number should end with ">"') read(unit=buf2(2:l1 - 1), fmt=*) a%number read(unit=buf2(l1 + 1:l2 - 1), fmt=*) a%abserr ENDSUBROUTINE READ_ERR
! Writing one err-real without advancing
! Prepends a space, may be eaten by carriage control.
SUBROUTINE WRITE_ERR(LUN, PRECISION, A) integer :: lun, precision, length type(err_real) :: a character(len = 80) :: rtf character(len=3) :: l, p
selectcase (N) case (INFO) write(*,*) string case (WARN) write(*,*) string
nwarn = nwarn + 1 case (ERROR) write(*,*) string
nerr = nerr + 1 case (FATAL) write(*,*) string stop'Fatal error' case default stop'err_error: bad error number ' endselect if (nwarn >= MAXWARN) stop'err_error: too many warnings, aborting ' if (nerr >= MAXERR) stop'err_error: too many errors, aborting ' return ENDSUBROUTINE ERR_ERROR
ENDMODULE ERR_ARITHMETIC
program test use num_consts ! Numerics module use err_arithmetic ! Error analytic module type(err_real) :: x, y, z ! Declare our vars
call num_check() ! Verify we have PRECISE reals
call write_txt_nl(6, "*** z = x / x") ! Write a line of text
x = err_real(1.0, 0.1) ! Assign: x = <1.0,0.1> call write_one(6, 5, "x = ", x) ! Write X
y = x ! Assign: y = x call write_one(6, 5, "y = ", y) ! Write Y
z = x / y ! Error analytic division call write_one(6, 5, "x/y = ", z) ! Write result call write_nl(6) ! Write a newline
call write_txt_nl(6, "*** singularity") ! Write a line of text call write_one(6, 5, "x = ", x) ! Write X
y = err_real(0.0, 0.1) ! Assign: y = <0.0,0.1> call write_one(6, 5, "y = ", y) ! Write Y
z = x / y ! Error analytic division call write_one(6, 5, "x/y = ", z) ! Write result call write_nl(6) ! Write a newline
call write_txt_nl(6, "*** trigo... ") ! Write a line of text
x = err_real(0.0, 0.1) ! Assign: y = <0.0,0.1> call write_one(6, 5, "x = ", x) ! Write X
z = sin(x) ! Error analytic sin(x) call write_one(6, 5, "sin(x) = ", z) ! Write sin(x)
z = cos(x) ! Error analytic cos(x) call write_one(6, 5, "cos(x) = ", z) ! Write cos(x)
z = tan(x) ! Error analytic tan(x) call write_one(6, 5, "tan(x) = ", z) ! Write tan(x) call write_nl(6) ! Write a newline
call write_txt_nl(6, "*** Interactive") ! Write a line of text call read_err(5, x, "Enter x: ") ! Input X call read_err(5, y, "Enter y: ") ! Input Y
z = x / y ! Error analytic division call write_one(6, 5, "x/y = ", z) ! Write result call write_nl(6) ! Write a newline
endprogram test
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.