TINY(x) intrinsic function

Viewed 829

I am a university lecturer, and I will teach the Numerical Methods course this semester using Fortran 90/95 as the programming language. The beginning of the course starts with the representation of numbers, and I would like to talk about the limits of numbers that can be represented with REAL(4), REAL(8) and REAL(16). I intend to use the following code on OnlineGDB (so that students won't have to install anything on their computers, which may be a pain in times of remote learning):

Program declare_reals

implicit none 

real(kind = 4)  :: a_huge, a_tiny ! single precision ; default if kind not specified

!real(4) :: a ! Equivalent to real(kind = 4) :: a

a_huge = huge(a_huge)
print*, "Max positive for real(4) : ", a_huge
a_tiny = tiny(a_tiny)
print*, "Min positive for real(4) : ", a_tiny 
print*, 

End Program declare_reals

With this code, I get

 Max positive for real(4) :    3.40282347E+38                                                                                                                          
 Min positive for real(4) :    1.17549435E-38

However, if I write a_tiny = tiny(a_tiny)/2.0, the output becomes

Min positive for real(4) :    5.87747175E-39

Looking at the documentation for gfortran (which OnlineGDB uses as the f95 compiler), I had the impression that anything below tiny(x) could result in an underflow and zero would show instead of a non-zero number. Could anyone help me understand what is happening here? If tiny(x) doesn't yield the smallest positive representable number, what is being shown due to the function call?

1 Answers

The Fortran Standard states the following about a real value:

The model set for real x is defined by

model representation of a real according to Fortran

where b and p are integers exceeding one; each fk is a non-negative integer less than b, with f1 nonzero; s is +1 or −1; and e is an integer that lies between some integer maximum emax and some integer minimum emin inclusively. For x = 0, its exponent e and digits fk are defined to be zero. The integer parameters b, p, emin, and emax determine the set of model floating-point numbers.

Real values which satisfy this definition, are referenced to be model numbers or normal floating point numbers. The floating point numbers your system can represent, i.e. the machine-representable numbers are a superset of the model numbers. They can, but not necessarily must, include the values with f1 zero — also known as subnormal floating point numbers — and are there to fill the underflow gap around zero.

The Fortran functions tiny(x), huge(x), epsilon(x), spacing(x) are all defined for model numbers.

The value of tiny(x) is given by bemin − 1, which for a single-precision floating-point number (binary32) is given by 2−126 and is the smallest model (normal) number. When your system follows IEEE754, the machine representable numbers will also contain the subnormal numbers. The smallest subnormal positive number is given bytiny(x)*epsilon(x) which in binary32 is 2−126 × 2−23. This explains why you can divide tiny(x) by two, i.e. the transition from normal to subnormal.

# smallest normal number
0 00000001 000000000000000000000002 = 0080 000016 = 2−126 ≈ 1.1754943508 × 10−38
# smallest subnormal number
0 00000000 000000000000000000000012 = 0000 000116 = 2−126 × 2−23 ≈ 1.4012984643 × 10−45

Note: when you divide tiny(x)*epsilon(x) by two, gfortran returns an arithmetic underflow error.


Ref: values taken from Wikipedia: Single precision floating-point format

Related