Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29233 Discussions

Need function to round a double to 12 significant figures

Andrew_Smith
Valued Contributor I
961 Views
I searched for rounding code but found all (and my own first attempt) suffered from integer overflow when using 12 significant figures. My new code makes use of a variable format internal write. The Fortran runtime appears to have a suitable rounding capability used for the IO but is it exposed to the user via any extension?

[bash]pure doubleprecision function roundNominal(num, n) result(res)
   doubleprecision, intent(in) :: num
   integer, intent(in) :: n
   character*25 temp
   if (isNaN(num)) then
      res = num
      return
   end if
   if (num == 0.0) then
      res = 0.0
      return
   end if
   write(temp, 10) num
   read(temp, *) res
10 format(0p, d25.)
end function
[/bash]
0 Kudos
5 Replies
mecej4
Honored Contributor III
961 Views
Please elaborate on why you want to do the rounding. Is it only for decimal conversion in conjunction with I/O, or is it to examine the effect of rounding on remaining calculations?

Will truncation do, instead? Will truncation of, say, 12 bits of the signifand do? If so, here is an example. It uses the deprecated EQUIVALENCE statement, but you can rewrite it with TRANSFER.

[fortran]program chop12x

real*8 x,y
integer*4 ix(2),iy(2)
equivalence (ix,x),(iy,y)

x=acos(0d0)*2d0
y=x
call chop12(y)
write(*,'(1x,F20.16,4x,2Z8)')x,ix(2),ix(1),y,iy(2),iy(1)

contains

subroutine chop12(x)
  real*8 x,r8
  integer*4 i4(2)
  equivalence(r8,i4)

  r8=x
  i4(1)=iand(i4(1),Z'FFFFF000')
  x=r8
return
end subroutine chop12

end program chop12x
[/fortran]
The result of running this program:

[bash]   3.1415926535897930    400921FB54442D18
   3.1415926535883050    400921FB54442000
[/bash]

0 Kudos
jimdempseyatthecove
Honored Contributor III
961 Views

Consider:

[fortran]pure doubleprecision function roundNominal(num, n) result(res)
   doubleprecision, intent(in) :: num
   integer, intent(in) :: n
   character*25 temp
   if (isNaN(num)) then
      res = num
      return
   end if
   if (num == 0.0) then
      res = 0.0
      return
   end if
   if(n == 0) then
      res = num
   end if
   if(n .gt. 0) then
      res = DNINT(num*DBLE(n)) / DBLE(n)
   else
      res = DNINT(num/DBLE(-n)) * DBLE(-n)
   endif
end function[/fortran]



Jim Dempsey

0 Kudos
Andrew_Smith
Valued Contributor I
960 Views
Thankyou both. DNINT will obviously do what I want. I should have searched a bit more before posting this.
0 Kudos
jimdempseyatthecove
Honored Contributor III
961 Views
Also note that temp should be removed, and you should use double precision literals for 0.0 (0.0D0)

Jim Dempsey

0 Kudos
Steven_L_Intel1
Employee
960 Views
Please use the generic ANINT rather than the specific DNINT. In general, I recommend against using specific functions unless you are passing them as actual arguments.
0 Kudos
Reply