
forums.silverfrost.com Welcome to the Silverfrost forums

View previous topic :: View next topic 
Author 
Message 
LitusSaxonicum
Joined: 23 Aug 2005 Posts: 2310 Location: Yateley, Hants, UK

Posted: Tue Jan 18, 2022 11:37 am Post subject: Rounding 


I think that everyone knows about rounding to the nearest INTEGER from a REAL with the conversion to INTEGER rounding down, so for rounding up you need to add 1, and to round to the nearest, add 0.5 before the conversion.
My issue is with rounding to a specific precision. The context is that in picking a coordinate off a screen, the position is given in pixels, which are INTEGER and therefore of limited precision anyway. Where the screen image represents a physical object which may have dimensions in (say) metres, there has to be a realworldtopixel scale factor from which I can determine the realworld coordinates of the point picked. Inevitably, this comes out with a huge string of decimal places. I have found that to round to a particular precision, say millimetres, I can get the effect I want with an internal WRITE, as in:
Code:  CHARACTER*(2) TEMP_TEXT
WRITE (TEMP_TEXT,’(F20.3)’) VALUE
READ (TEMP_TEXT,*) VALUE 
It occurs to me that inside the WRITE statement there has to be a rounding algorithm, and I have gone a roundabout way to using it.
So far, so good. The method works if I want to round to a particular number of decimal places, and before anyone says, yes, I know that the binary representation will be inexact. The problem is not solved by this method if I want to round to (say) the nearest 0.2m, 0.25m or 0.5m. My efforts so far do work, but are clumsy.
Is there anyone out there with a mathematics and computing background who knows of a good general purpose rounding algorithm?
Eddie 

Back to top 


mecej4
Joined: 31 Oct 2006 Posts: 1680

Posted: Tue Jan 18, 2022 1:29 pm Post subject: 


Eddie,
There are probably additional requirements that you have not yet stated, but here is my first attempt. Given integer arrays iX and iY, which contain pixel coordinates, the second part of the following program outputs the corresponding "real world" X and Y coordinates that are "snapped" to the nearest integer multiples of 0.1 and 0.25, respectively.
Code:  program round
implicit none
integer, parameter :: NP = 10
integer i, ix(NP), iy(NP)
real x(NP), y(NP)
real, parameter :: xunit = 0.005, yunit = 0.01 ! real world distance per pixel
real, parameter :: xsnap = 0.1, ysnap = 0.25 ! smallest discrete distances allowed
call random_number(x)
call random_number(y)
x = x*640; y = y*480
ix = nint(x); iy = nint(y) ! ix and iy are artifically generated pixel coordinates
! Eddie's real app starts here, with ix, iy generated by mouse clicks, etc.
print '(i2,2i8)',(i,ix(i),iy(i),i=1,NP)
print *
x = nint(ix*xunit/xsnap)*xsnap
y = nint(iy*yunit/xsnap)*ysnap
print '(i2,2F10.2)',(i,x(i),y(i),i=1,NP)
end program 
The output:
Code:  1 431 3
2 212 360
3 94 260
4 475 203
5 220 209
6 58 263
7 471 127
8 335 43
9 393 77
10 429 350
1 2.20 0.00
2 1.10 9.00
3 0.50 6.50
4 2.40 5.00
5 1.10 5.25
6 0.30 6.50
7 2.40 3.25
8 1.70 1.00
9 2.00 2.00
10 2.10 8.75 
Last edited by mecej4 on Tue Jan 18, 2022 2:55 pm; edited 1 time in total 

Back to top 


LitusSaxonicum
Joined: 23 Aug 2005 Posts: 2310 Location: Yateley, Hants, UK

Posted: Tue Jan 18, 2022 2:54 pm Post subject: 


Thanks Mecej4,
Not quite it, though, because after I have got the pixel coords, I convert to real world coordinates, and it's those I want to round.
Hence, after the random number generation and multiplying by the VGA 640x480 in your demo, I get:
1 430.76504517 43.13289642 (printing 8 decimal places)
and the output I want, rounding to say the nearest 0.1 is:
1 430.8 43.1
or to 0.25 is:
1 430.75 43.25
and so on.
Thanks for your suggestion, which provides me with food for thought.
Eddie 

Back to top 


mecej4
Joined: 31 Oct 2006 Posts: 1680

Posted: Tue Jan 18, 2022 3:16 pm Post subject: 


Please rethink your response!
You don't start with 430.765 pixels, etc. You start with integers.
You have to apply a linear transformation to scale and shift the integer valued pixel coordinates to real world coordinates. You then snap the real coordinates to the chosen grid.
The expressions ix*xunit and iy*yunit are the real world coordinates. Dividing by xsnap, rounding and then multiplying by xsnap perfoms the snapping operation to the x coordinates, and similarly ysnap is applied for the y coordinates.
Perhaps you can make up an example with a small number of input pixel coordinates and the desired output real coordinates snapped to a grid.

However, if the customer is always right, here goes:
Code:  program snap
implicit none
real :: x(2) = [430.765, 43.133]
real :: snap = 0.25
print '(2F8.2)',nint(x/snap)*snap
end 


Back to top 


LitusSaxonicum
Joined: 23 Aug 2005 Posts: 2310 Location: Yateley, Hants, UK

Posted: Wed Jan 19, 2022 12:30 pm Post subject: 


Hi Mecej4,
I hadn't realised I was a customer. What do you want your fee in? I have a stock of predecimal coins and old Italian Lira.
I see now what you did, I was confused by 2 axis scaling  I have the same scales in both axes (it's a real object, not a graph/chart). Also, the object is centred in the graphics workspace (so there are offsets in both axes) , and being ClearWin/Windows, y is measured downwards, which I know from experience of myself and others isn't the upper right quadrant that many engineers like myself are more comfortable in. Hence I hadn't imagined the 'descaling' being done in the same breath as the rounding.
There is an additional subtlety about whether or not the centre or corner of a pixel is the critical start point when descaling, so it might not necessarily be an integer number of pixels (is it the pixel number off the graphics area minus 0.5?).
Doesn't it have to be:
Code:  nint (x/snap+0.5)*snap 
to both round up or down to nearest?
Incidentally, I'm doing it in REAL*8, not REAL*4 where there isn't enough decimal places of precision  but you probably guessed that.
What I described as my clumsy first attempts consisted of only operating on the decimal part of the realworld coordinates .
Thanks for thinking about it.
Eddie 

Back to top 


mecej4
Joined: 31 Oct 2006 Posts: 1680

Posted: Wed Jan 19, 2022 3:16 pm Post subject: 


The advantage of using the NINT function instead of the INT function is that you do not have to tinker with the +0.5 or 0.5, etc.
For x >= 0, NINT(x) = INT(x+0.5)
For x < 0, NINT(x) = INT(x0.5)
Loosely stated, INT is for chopping, NINT for rounding.
You have to decide for yourself the convention for the location of a pixel (which of its four corners, the center, or the four centers of its four edges, as appropriate for your intended application. 

Back to top 


LitusSaxonicum
Joined: 23 Aug 2005 Posts: 2310 Location: Yateley, Hants, UK

Posted: Wed Jan 19, 2022 4:15 pm Post subject: 


Yep, didn't realise what NINT did  I thought it was yet another botch of things that have been there forever and was an alias for INT rather than INT dressed up so as to change its personal pronouns.
Quote:  You have to decide for yourself the convention for the location of a pixel (which of its four corners, the centre or the four centres of its four edges, as appropriate for your intended application. 
... which is why I started by asking for rounding only, as I was taking care of that already!
Anyway, I learned something today, which shows that some old dogs can still learn new tricks, even if they are simple ones!
Many thanks
Eddie 

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
