forums.silverfrost.com
Welcome to the Silverfrost forums

Author Message
LitusSaxonicum

Joined: 23 Aug 2005
Posts: 2315
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 real-world-to-pixel scale factor from which I can determine the real-world 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
mecej4

Joined: 31 Oct 2006
Posts: 1692

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
LitusSaxonicum

Joined: 23 Aug 2005
Posts: 2315
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
mecej4

Joined: 31 Oct 2006
Posts: 1692

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

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
LitusSaxonicum

Joined: 23 Aug 2005
Posts: 2315
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 pre-decimal 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 'de-scaling' 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 real-world coordinates .

Eddie
mecej4

Joined: 31 Oct 2006
Posts: 1692

 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(x-0.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.
LitusSaxonicum

Joined: 23 Aug 2005
Posts: 2315
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
 Display posts from previous: All Posts1 Day7 Days2 Weeks1 Month3 Months6 Months1 Year Oldest FirstNewest First
 All times are GMT + 1 Hour Page 1 of 1