|
forums.silverfrost.com Welcome to the Silverfrost forums
|
View previous topic :: View next topic |
Author |
Message |
AlisonV
Joined: 14 Sep 2015 Posts: 4 Location: Brisbane
|
Posted: Tue Sep 15, 2015 12:27 am Post subject: What is the nearest integer that NINT() rounds to? |
|
|
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 |
|
|
JohnCampbell
Joined: 16 Feb 2006 Posts: 2554 Location: Sydney
|
Posted: Tue Sep 15, 2015 2:13 am Post subject: |
|
|
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 |
|
|
mecej4
Joined: 31 Oct 2006 Posts: 1886
|
Posted: Tue Sep 15, 2015 3:46 am Post subject: Re: What is the nearest integer that NINT() rounds to? |
|
|
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 |
|
|
AlisonV
Joined: 14 Sep 2015 Posts: 4 Location: Brisbane
|
Posted: Wed Sep 16, 2015 4:14 am Post subject: |
|
|
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 |
|
|
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 7924 Location: Salford, UK
|
Posted: Wed Sep 16, 2015 7:03 am Post subject: |
|
|
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 |
|
|
davidb
Joined: 17 Jul 2009 Posts: 560 Location: UK
|
Posted: Wed Sep 16, 2015 7:10 pm Post subject: |
|
|
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 |
|
|
LitusSaxonicum
Joined: 23 Aug 2005 Posts: 2388 Location: Yateley, Hants, UK
|
Posted: Wed Sep 16, 2015 7:30 pm Post subject: |
|
|
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 |
|
|
JohnCampbell
Joined: 16 Feb 2006 Posts: 2554 Location: Sydney
|
Posted: Wed Sep 16, 2015 11:15 pm Post subject: |
|
|
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 |
|
|
mecej4
Joined: 31 Oct 2006 Posts: 1886
|
Posted: Wed Sep 16, 2015 11:34 pm Post subject: |
|
|
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 |
|
|
alex21
Joined: 20 Apr 2011 Posts: 75 Location: Australia
|
Posted: Thu Sep 17, 2015 2:37 am Post subject: Math.Round Method |
|
|
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 |
|
|
AlisonV
Joined: 14 Sep 2015 Posts: 4 Location: Brisbane
|
Posted: Thu Sep 17, 2015 9:46 am Post subject: |
|
|
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 |
|
|
IanLambley
Joined: 17 Dec 2006 Posts: 490 Location: Sunderland
|
Posted: Thu Sep 17, 2015 12:14 pm Post subject: |
|
|
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 |
|
|
mecej4
Joined: 31 Oct 2006 Posts: 1886
|
Posted: Thu Sep 17, 2015 1:52 pm Post subject: |
|
|
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 |
|
|
AlisonV
Joined: 14 Sep 2015 Posts: 4 Location: Brisbane
|
Posted: Fri Sep 18, 2015 2:02 am Post subject: |
|
|
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 |
|
|
|
|
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
|