Here is a version of your code that incorporates the features of this discussion.
program fp
implicit none
integer, parameter :: WP = selected_real_kind( p=6 )
real(WP), parameter :: BIG = 1.0_wp / tiny( 1.0_wp )
real(WP), parameter :: FOO = 0.12_wp*BIG
real(WP), parameter :: BAR = 1.2_wp*BIG
real(WP) :: x(5), r
integer :: iexp
intrinsic :: EXPONENT, SCALE
print *, 'BIG=', BIG, ' BAR=', BAR
if ( BAR > BIG ) then
print *, 'Warning: Beware of divisions and reciprocals with BAR.'
iexp = 16 - EXPONENT(BAR)
else
print *, 'Notice: Divisions and reciprocals are safe with BAR.'
iexp = 0
end if
print *
blk1: block
print *, "Block 1: unrolled division"
x = FOO
print *, "x = ", x
x(1) = x(1) / BAR ; x(2) = x(2) / BAR ; x(3) = x(3) / BAR
x(4) = x(4) / BAR ; x(5) = x(5) / BAR
print *, "After division: x = ", x
end block blk1
print *
blk2: block
print *, "Block 2: vector division"
x = FOO
print *, "x = ", x
x = x / BAR
print *, "After division: x = ", x
end block blk2
print *
blk3: block
print *, "Block 3: unrolled reciprocal"
x = FOO
print *, "x = ", x
r = 1.0_wp / BAR
x(1) = x(1) * r ; x(2) = x(2) * r ; x(3) = x(3) * r
x(4) = x(4) * r ; x(5) = x(5) * r
print *, "After multiplication x = ", x
end block blk3
print *
blk4: block
print *, "Block 4: vector reciprocal"
x = FOO
print *, "x = ", x
r = 1.0 / BAR
x = x * r
print *, "After multiplication: x = ", x
end block blk4
print *
blk5: block
print *, 'Block5: safe scaled vector division with iexp=', iexp
x = FOO
print *, "x = ", x
x = SCALE( x, iexp ) / SCALE( BAR, iexp )
print *, "After division: x = ", x
end block blk5
print *
blk6: block
print *, 'Block6: safe scaled vector reciprocal with iexp=', iexp
x = FOO
print *, "x = ", x
r = 1.0_wp / SCALE( BAR, iexp )
x = SCALE( x, iexp ) * r
print *, "After multiplication: x = ", x
end block blk6
end program fp
With ifort default options, here is the output.
$ ifort fp.f90 && a.out
BIG= 8.5070592E+37 BAR= 1.0208471E+38
Warning: Beware of divisions and reciprocals with BAR.
Block 1: unrolled division
x = 1.0208471E+37 1.0208471E+37 1.0208471E+37 1.0208471E+37 1.0208471E+37
After division: x = 0.1000000 0.1000000 0.1000000
0.1000000 0.1000000
Block 2: vector division
x = 1.0208471E+37 1.0208471E+37 1.0208471E+37 1.0208471E+37 1.0208471E+37
After division: x = 0.0000000E+00 0.0000000E+00 0.0000000E+00
0.0000000E+00 0.1000000
Block 3: unrolled reciprocal
x = 1.0208471E+37 1.0208471E+37 1.0208471E+37 1.0208471E+37 1.0208471E+37
After multiplication x = 0.0000000E+00 0.0000000E+00 0.0000000E+00
0.0000000E+00 0.0000000E+00
Block 4: vector reciprocal
x = 1.0208471E+37 1.0208471E+37 1.0208471E+37 1.0208471E+37 1.0208471E+37
After multiplication: x = 0.0000000E+00 0.0000000E+00 0.0000000E+00
0.0000000E+00 0.0000000E+00
Block5: safe scaled vector division with iexp= -111
x = 1.0208471E+37 1.0208471E+37 1.0208471E+37 1.0208471E+37 1.0208471E+37
After division: x = 0.1000000 0.1000000 0.1000000
0.1000000 0.1000000
Block6: safe scaled vector reciprocal with iexp= -111
x = 1.0208471E+37 1.0208471E+37 1.0208471E+37 1.0208471E+37 1.0208471E+37
After multiplication: x = 0.1000000 0.1000000 0.1000000
0.1000000 0.1000000
As I think is clear, the problems are entirely due to whether the reciprocal+multiplication is used rather than the division when evaluating the expression. When the reciprocal is used, then the result depends on whether the denormals are set to zero. If the operands are scaled as in the last two cases, there are no problems, no surprises, with either expression.
I do not believe this is unique to ifort. I think with the appropriate optimization and compiler options, one can probably get any compiler to produce this same output. I personally also think that this is not a compiler bug. I think the ifort default choices are legitimate, and when these kinds of issues occur due to floating point quirks, it is up to the programmer to fix them. In general, this can be done by rearranging expressions to avoid the overflow/underflow situations, or by scaling the whole problem to avoid such issues.
A developer has the choice of fixing these kinds of problems once and forever in the source code, or of using the right magical combination of compiler options every time the code is compiled. Since it is impractical for a programmer to specify the correct compiler options for every current and future compiler, it is best for the problem to be corrected in the source code. And preferably with sufficient comments and explanations so that other programmers do not mistakenly change the code and reintroduce the problem in the future.
I also think, as a quality of implementation issue, that when such denormal operations occur that the compiler should somehow warn the programmer at run time. This allows the programmer to fix the problem. When these things occur silently, they are a land mine waiting to explode at the worst possible time. Certainly there are many situations where denormals can be set to zero without causing problems, but it should be up to the programmer to make that decision, it should not be done silently by the compiler.