forums.silverfrost.com Forum Index forums.silverfrost.com
Welcome to the Silverfrost forums
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

What is the nearest integer that NINT() rounds to?

 
Post new topic   Reply to topic    forums.silverfrost.com Forum Index -> Support
View previous topic :: View next topic  
Author Message
AlisonV



Joined: 14 Sep 2015
Posts: 4
Location: Brisbane

PostPosted: Tue Sep 15, 2015 12:27 am    Post subject: What is the nearest integer that NINT() rounds to? Reply with quote

realvar = 31.5
intvar = NINT(realvar)


When I compile these lines into a DLL (which is called by a C# GUI), intvar shows the value 31.
When I compile these lines as part of an EXE, intvar shows the value 32. I'm using the silverfrost compiler in both cases, with reals set to kind=2.

Is this behaviour expected? If it is, is there a way to force 0.5 to be rounded up (or at least to show the same value)?

Many thanks
Back to top
View user's profile Send private message
JohnCampbell



Joined: 16 Feb 2006
Posts: 2554
Location: Sydney

PostPosted: Tue Sep 15, 2015 2:13 am    Post subject: Reply with quote

I don't have an explanation for the DLL performance.
Is the expression "realvar = 31.5" exactly as you have typed ?
If the result of getting to 31.5 has some rounding noise so was less than 31.5, you would get the result 31.

31.5 when converted into a binary real is an exact representation, as there are lots of trailing zeros.
31.5 = 2^4 + 2^3 + 2^2 + 2^1 +2^0 + 2^(-1)

It should round up to 32.

The following code tries to demonstrate how 31.5 is mostly zeroes, but noise could change that.
Code:
real*4    realval
real*8    dblval
integer*4 intval
integer*8 jval
!
 1 format (/a)
  write (*,1) ' represented as 31.5'
  realval = 31.5
  intval = nint (realval)
  write (*,*) 'realval=',realval
  write (*,*) 'intval =',intval
!
  write (*,*) ' integer equivalent of real*4'
  intval = transfer (realval,intval)
  write (*,*) 'intval =',intval
  write (*,'(a,o0,a)') ' octal  = ',intval, ' shows mostly 0'
!
  write (*,1) ' represented as 31.5 less noise'
  realval = nearest (realval,-1.0)  ! nearest real less than 31.5
  intval = nint (realval)
  write (*,*) 'realval=',realval
  write (*,*) 'intval =',intval
  intval = transfer (realval,intval)
  write (*,*) 'intval =',intval
  write (*,'(a,o0,a)') ' octal  = ',intval, ' no longer mostly 0'
!
  write (*,1) ' represented as 63.0'
  realval = 31.5
  realval = realval*2
  intval = transfer (realval,intval)
  write (*,*) 'intval =',intval
  write (*,'(a,o0,a)') ' octal  = ',intval, ' shows mostly 0'
!
  write (*,1) ' represented as 31.5_8'
  dblval = 31.5
  intval = nint (dblval)
  write (*,*) 'dblval =',dblval
  write (*,*) 'intval =',intval
!
  write (*,*) ' integer equivalent of real*8'
  jval = transfer (dblval,jval)
  write (*,*) 'jval =',jval
  write (*,'(a,o0,a)') ' octal  = ',jval, ' again shows mostly 0'
!
end
Back to top
View user's profile Send private message
mecej4



Joined: 31 Oct 2006
Posts: 1886

PostPosted: Tue Sep 15, 2015 3:46 am    Post subject: Re: What is the nearest integer that NINT() rounds to? Reply with quote

AlisonV wrote:
realvar = 31.5
intvar = NINT(realvar)


When I compile these lines into a DLL (which is called by a C# GUI), intvar shows the value 31.
When I compile these lines as part of an EXE, intvar shows the value 32. I'm using the silverfrost compiler in both cases, with reals set to kind=2.

Is this behaviour expected? If it is, is there a way to force 0.5 to be rounded up (or at least to show the same value)?


Yes, when you have a value that is equidistant from two integers, which integer is nearer?

If you want to round up, use the CEILING function.
Back to top
View user's profile Send private message
AlisonV



Joined: 14 Sep 2015
Posts: 4
Location: Brisbane

PostPosted: Wed Sep 16, 2015 4:14 am    Post subject: Reply with quote

Thanks Everyone. I'm sure precision must play a part in this .. but this happens dependent on whether the code is compiled by Silverfrost as a DLL or EXE. All other compiler settings are the same (as is possible).

Here's an expanded code block to better demonstrate the issue. The code is part of a calculation for percentiles - ivar selects the array element to return for a given percentile.

REAL :: rvar
INTEGER ::ivar,n,pc

n=45
pc=30
rvar= REAL(n)*((100.0-REAL(pc))/100.0)
ivar = NINT(rvar)


When I place the above self-contained code inside a DLL - the value for ivar is 31.
However, when I place this exact same code inside an EXE, the value for ivar is 32.
When I set rvar = 31.5, ivar does return 32 for both the DLL and EXE (apologies for misleading in previous post).

The DLL is Fortran Subroutine Main compiled in CLR mode as a DLL within a C# project. The EXE is the same Subroutine Main code, called by a Fortran input and output program.


Last edited by AlisonV on Thu Sep 17, 2015 10:08 am; edited 1 time in total
Back to top
View user's profile Send private message
PaulLaidler
Site Admin


Joined: 21 Feb 2005
Posts: 7924
Location: Salford, UK

PostPosted: Wed Sep 16, 2015 7:03 am    Post subject: Reply with quote

Try using /explist on the FTN95 command line.
The resulting .lis file will show how the calculation proceeds in the two cases.
Back to top
View user's profile Send private message AIM Address
davidb



Joined: 17 Jul 2009
Posts: 560
Location: UK

PostPosted: Wed Sep 16, 2015 7:10 pm    Post subject: Reply with quote

It isn't unusual for compilers to generate different assembly for the same code. You may just have one value that is a bit smaller than 31.5 and another that is a bit larger. In which case both answers are correct.

You could try re-writing your expression to minimise roundoff errors, something like:

Code:

rvar= REAL(n)*(0.5 + (0.5 - 0.01*REAL(pc)))

_________________
Programmer in: Fortran 77/95/2003/2008, C, C++ (& OpenMP), java, Python, Perl
Back to top
View user's profile Send private message
LitusSaxonicum



Joined: 23 Aug 2005
Posts: 2388
Location: Yateley, Hants, UK

PostPosted: Wed Sep 16, 2015 7:30 pm    Post subject: Reply with quote

Sometimes, a standard function isn't the answer (particularly when I can't remember that one exists), and you have to do it yourself. A real to integer assignment always rounds down, and if you want to round up, then add 1 after rounding down.

You then get to decide how the difficult cases are dealt with when you code it yourself. Davidb's approach is a DIY round to nearest that lets you know what happens in every case.

Eddie
Back to top
View user's profile Send private message
JohnCampbell



Joined: 16 Feb 2006
Posts: 2554
Location: Sydney

PostPosted: Wed Sep 16, 2015 11:15 pm    Post subject: Reply with quote

What does the rounding of exactly 31.5 mean anyway ? by convention 32
realval = nearest (31.5,-1.0) ! nearest real less than 31.5 is rounded to 31 ?

However shouldn't 31.4999999 be rounded to 31 ?
Given the rounding in the format writer, this becomes displayed as 31.500000 and so should be seen as being rounded to 32

In recording numbers in experiments, the meaning of rounding also becomes confused.
if the reading was initially 31.447
rounding to 2 sig fig becomes 31.45
then to 1 becomes 31.5
then to 0 becomes 32

So the history of the recording accuracy becomes an issue

This is the problem with roundoff
Back to top
View user's profile Send private message
mecej4



Joined: 31 Oct 2006
Posts: 1886

PostPosted: Wed Sep 16, 2015 11:34 pm    Post subject: Reply with quote

Repeated rounding with insufficient precision can produce absurd results. There is an infamous example from 1982, relating to the Vancouver stock exchange. From https://en.wikipedia.org/wiki/Vancouver_Stock_Exchange :
Quote:
The history of the exchange's index provides a standard case example of large errors arising from seemingly innocuous floating point calculations. In January 1982 the index was initialized at 1000 and subsequently updated and truncated to three decimal places on each trade. Such a thing was done about 3000 times each day. The accumulated truncations led to an erroneous loss of around 25 points per month. Over the weekend of November 25–28, 1983, the error was corrected, raising the value of the index from its Friday closing figure of 524.811 to 1098.892.
Back to top
View user's profile Send private message
alex21



Joined: 20 Apr 2011
Posts: 75
Location: Australia

PostPosted: Thu Sep 17, 2015 2:37 am    Post subject: Math.Round Method Reply with quote

Hello,

To clarify the code is behaving differently when it is compiled as Win32 vs .NET, I believe this relates to the default behavior of Math.Round() in the CLR which will round down from .5 unless MidpointRounding.AwayFromZero is specified.

Could this be what is leading to the difference in the behavior of the code when compiled for .NET vs Win32?

Thanks,
Alex.
Back to top
View user's profile Send private message MSN Messenger
AlisonV



Joined: 14 Sep 2015
Posts: 4
Location: Brisbane

PostPosted: Thu Sep 17, 2015 9:46 am    Post subject: Reply with quote

I've totally missed PaulLaidler's post above. Below is part of the .lis file for the main subroutine when running it within the DLL. Any suggestions as to a solution would be welcome!

Thanks again,
Alison

Code:

Silverfrost FTN95/.NET Ver 7.20.0  Main.F95  Thu Sep 17 19:02:14 2015

Compiler Options in Effect:
    BINARY BOUNDS_CHECK CHECK CHECKMATE CLR CLR_VER COLOUR DEBUG
    DEFREAL_KIND 2 DELETE_OBJ_ON_ERROR ERROR_NUMBERS EXPLIST F2K FPP FULL_DEBUG MINIMISE_REBUILD 
    NO_BANNER NO_QUIT NON_STANDARD REFERENCE SINGLE_THREADED UNDEF UNLIMITED_ERRORS VS8
    WIDE_SOURCE WRAP

...skipping down to...

   ;;(89)        ldloc.s   RVAR1
      ;;(90)        dup       
      ;;(91)        ldc.r8    0.0
      ;;(93)        blt       __N4
      ;;(94)        ldc.r8    0.5
      ;;(95)        add       
      ;;(95)        call      System.Math.Floor
      ;;(97)        br        __N5
      ;;(98)     Label     __N4:
      ;;(99)        ldc.r8    0.5
      ;;(100)       sub       
      ;;(100)       call      System.Math.Ceiling
      ;;(101)    Label     __N5:
      ;;(102)       conv.ovf.i4 

  ...
etc
Back to top
View user's profile Send private message
IanLambley



Joined: 17 Dec 2006
Posts: 490
Location: Sunderland

PostPosted: Thu Sep 17, 2015 12:14 pm    Post subject: Reply with quote

Try this and see what happens.
Code:

      real*4 realval
      realval = 31.5e0
      print *,int(realval+0.5e0),realval


or
Code:

      real*4 realval
      realval = 31.5e0
      print *,sign(int(abs(realval)+0.5e0),realval),realval
Back to top
View user's profile Send private message Send e-mail
mecej4



Joined: 31 Oct 2006
Posts: 1886

PostPosted: Thu Sep 17, 2015 1:52 pm    Post subject: Reply with quote

The issue is related to basic binary real arithmetic and not to DLL/EXE or native/CLR.

Here is what happened. Consider the expression given in #1:
Code:
rvar= REAL(n)*((100.0-REAL(pc))/100.0)

Since pc=30, what happens (leaving optimizations aside) is that in the FPU we subtract 30.0 from 100.0, then divide by 100.0. We expect all this to yield as the result the seemingly simple number 0.70. This number, after multiplying by 45, should give us 31.5, which we then wish to round and obtain the nearest integer.

Unfortunately, the number 0.70 has no exact IEEE 32-bit representation, and the value in the FPU, 3F333333H, is a truncation of the infinitely precise binary representation of 0.70. (This is similar to the decimal number 0.333333 being a tad less than 1/3)

We may think of 3F333333H as what remains after we chop off the infinite sequence of nybbles with the value 3 from the right hand end. Thus, the number in the FPU is a tiny bit less than 0.70. When this is multiplied by REAL(n)=45.0, (which, itself, is exactly represented as 42340000H) the result is not the expected 31.5, exactly half-way between the lovely integers 31 and 32, but 31.49999..., which when given to NINT gives us the unwanted/unexpected result of 31.

The O.P. needs to rethink the use of parentheses, etc., in the expression in such a way as to obtain the kind of rounding desired. See what happens if, instead of the line above, you write
Code:
rvar= REAL(n)*(100.0-REAL(pc))/100.0


Davidb and others have written about similar variations above.
Back to top
View user's profile Send private message
AlisonV



Joined: 14 Sep 2015
Posts: 4
Location: Brisbane

PostPosted: Fri Sep 18, 2015 2:02 am    Post subject: Reply with quote

Thanks heaps mecej4 and everyone! Removing the brackets did allow a larger value to be divided by the 100.0, resulting in the desired answer of 32 for both the DLL and EXE. I am now getting the same (expected) answers for the percentile calculations from both DLL and EXE forms of the Main code.

Thanks also for the explanation - I will keep in mind the need to use brackets more carefully when dealing with double precision numbers.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    forums.silverfrost.com Forum Index -> Support All times are GMT + 1 Hour
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group