[GRASS-dev] [GRASS GIS] #1976: Allow rounding of floating numbers

#1976: Allow rounding of floating numbers
-------------------------+--------------------------------------------------
Reporter: pvanbosgeo | Owner: grass-dev@…
     Type: enhancement | Status: new
Priority: normal | Milestone: 7.0.0
Component: Default | Version: unspecified
Keywords: | Platform: Unspecified
      Cpu: Unspecified |
-------------------------+--------------------------------------------------
The round() function in r.mapcalc always returns an integer, regardless of
its argument types. Integers are always 32-bit, so the result is limited
to the range +/- 2147483647 (2^31-1).

Suggestion: extend the function to allow to round numbers outside the
integer range, and to round with a specific number of decimal places, like
e.g., the function round() in R.

Various options have been discussed on the mailing list, see this
[http://lists.osgeo.org/pipermail/grass-dev/2013-May/063827.html email
thread]

--
Ticket URL: <http://trac.osgeo.org/grass/ticket/1976&gt;
GRASS GIS <http://grass.osgeo.org>

#1976: r.mapcalc: Allow rounding of floating numbers
-------------------------+--------------------------------------------------
Reporter: pvanbosgeo | Owner: grass-dev@…
     Type: enhancement | Status: new
Priority: normal | Milestone: 7.0.0
Component: Raster | Version: unspecified
Keywords: r.mapcalc | Platform: Unspecified
      Cpu: Unspecified |
-------------------------+--------------------------------------------------
Changes (by martinl):

  * keywords: => r.mapcalc
  * component: Default => Raster

--
Ticket URL: <http://trac.osgeo.org/grass/ticket/1976#comment:1&gt;
GRASS GIS <http://grass.osgeo.org>

#1976: r.mapcalc: Allow rounding of floating numbers
-------------------------+--------------------------------------------------
Reporter: pvanbosgeo | Owner: grass-dev@…
     Type: enhancement | Status: new
Priority: normal | Milestone: 7.0.0
Component: Raster | Version: unspecified
Keywords: r.mapcalc | Platform: Unspecified
      Cpu: Unspecified |
-------------------------+--------------------------------------------------

Comment(by mmetz):

Replying to [ticket:1976 pvanbosgeo]:
> The round() function in r.mapcalc always returns an integer, regardless
of its argument types. Integers are always 32-bit, so the result is
limited to the range +/- 2147483647 (2^31-1).
>
> Suggestion: extend the function to allow to round numbers outside the
integer range, and to round with a specific number of decimal places, like
e.g., the function round() in R.

Try trunk r56313.

The output type of round() is now the same like the input type. Rounding
to a given number of decimal places is supported with round(x, y) with y =
number of decimal places. The new function round(x, y) supports a negative
number of decimal places: for example, round(119, -1) results in 120, and
round(119, -2) results in 100.

--
Ticket URL: <http://trac.osgeo.org/grass/ticket/1976#comment:2&gt;
GRASS GIS <http://grass.osgeo.org>

#1976: r.mapcalc: Allow rounding of floating numbers
-------------------------+--------------------------------------------------
Reporter: pvanbosgeo | Owner: grass-dev@…
     Type: enhancement | Status: new
Priority: normal | Milestone: 7.0.0
Component: Raster | Version: unspecified
Keywords: r.mapcalc | Platform: Unspecified
      Cpu: Unspecified |
-------------------------+--------------------------------------------------

Comment(by pvanbosgeo):

Works, great work!!

--
Ticket URL: <http://trac.osgeo.org/grass/ticket/1976#comment:3&gt;
GRASS GIS <http://grass.osgeo.org>

"GRASS GIS" <trac@osgeo.org> writes:

#1976: r.mapcalc: Allow rounding of floating numbers
-------------------------+--------------------------------------------------
Reporter: pvanbosgeo | Owner: grass-dev@…
     Type: enhancement | Status: new
Priority: normal | Milestone: 7.0.0
Component: Raster | Version: unspecified
Keywords: r.mapcalc | Platform: Unspecified
      Cpu: Unspecified |
-------------------------+--------------------------------------------------

Comment(by mmetz):

Replying to [ticket:1976 pvanbosgeo]:
> The round() function in r.mapcalc always returns an integer, regardless
of its argument types. Integers are always 32-bit, so the result is
limited to the range +/- 2147483647 (2^31-1).
>
> Suggestion: extend the function to allow to round numbers outside the
integer range, and to round with a specific number of decimal places, like
e.g., the function round() in R.

Try trunk r56313.

The output type of round() is now the same like the input type. Rounding
to a given number of decimal places is supported with round(x, y) with y =
number of decimal places. The new function round(x, y) supports a negative
number of decimal places: for example, round(119, -1) results in 120, and
round(119, -2) results in 100.

<#secure method=pgpmime mode=sign>

Great - sounds like a perfect solution to me.

Thanks,

Rainer

--
Rainer M. Krug, PhD (Conservation Ecology, SUN), MSc (Conservation Biology, UCT), Dipl. Phys. (Germany)

Centre of Excellence for Invasion Biology
Stellenbosch University
South Africa

Tel : +33 - (0)9 53 10 27 44
Cell: +33 - (0)6 85 62 59 98
Fax : +33 - (0)9 58 10 27 44

Fax (D): +49 - (0)3 21 21 25 22 44

email: Rainer@krugs.de

Skype: RMkrug

#1976: r.mapcalc: Allow rounding of floating numbers
-------------------------+--------------------------------------------------
Reporter: pvanbosgeo | Owner: grass-dev@…
     Type: enhancement | Status: new
Priority: normal | Milestone: 7.0.0
Component: Raster | Version: unspecified
Keywords: r.mapcalc | Platform: Unspecified
      Cpu: Unspecified |
-------------------------+--------------------------------------------------

Comment(by glynn):

Replying to [comment:2 mmetz]:

> The output type of round() is now the same like the input type.

This changes long-standing behaviour in a way which could break scripts.
E.g. if k is an integer,
{{{
round(x) / k
}}}
would always evaluate to an integer, whereas now it may result in a
fraction.

> Rounding to a given number of decimal places is supported with round(x,
y)

From the mailing list discussion ...

Rounding to a given number of decimals is unnecessarily limiting. The
algorithm for generalised rounding is essentially:
{{{
         roundTo(x, k) = round(x / k) * k.
}}}
Rounding to N decimal places is just a case of using k=1/10^N^. If you
allow k to be specified directly, then you can round to arbitrary
steps (e.g. k=5 would round to the nearest multiple of 5, etc).

However: there's a slight problem with doing it that way: 0.1 isn't
exactly representable in binary, so e.g. x/0.1 isn't equal to x*10; it
would be more accurate to use:
{{{
         roundTo(x, k) = round(x * k) / k
}}}
where k is the reciprocal of the step, so k=10^N^ to round to N decimal
places (or k=2 to round to 1/2).

The downside is that the interface is less useful if you want to round
to something other than a fixed number of decimal places. E.g. if you
wanted to round to the nearest multiple of 45 degrees, you'd need to
use k=1.0/45, which isn't exactly representable.

Unless someone has a better idea, I plan to change the round() function so
that the second argument is the step value, and add an roundi() (round-
inverse) function where the second argument is the reciprocal of the step
value (to avoid the rounding error when using a step of 10^-N^).

--
Ticket URL: <http://trac.osgeo.org/grass/ticket/1976#comment:4&gt;
GRASS GIS <http://grass.osgeo.org>

#1976: r.mapcalc: Allow rounding of floating numbers
-------------------------+--------------------------------------------------
Reporter: pvanbosgeo | Owner: grass-dev@…
     Type: enhancement | Status: new
Priority: normal | Milestone: 7.0.0
Component: Raster | Version: unspecified
Keywords: r.mapcalc | Platform: Unspecified
      Cpu: Unspecified |
-------------------------+--------------------------------------------------

Comment(by mmetz):

Replying to [comment:4 glynn]:
> Replying to [comment:2 mmetz]:
>
> > The output type of round() is now the same like the input type.
>
> This changes long-standing behaviour in a way which could break scripts.

OK. Restoring the original behaviour for round(x) is easy. But it would be
nice to have a round(x, y) function that preserves the data type of x in
order to have a possibility to avoid integer overflow.

>
> > Rounding to a given number of decimal places is supported with
round(x, y)
>
> From the mailing list discussion ...
>
> Rounding to a given number of decimals is unnecessarily limiting. The
algorithm for generalised rounding is essentially:
>
{{{
         roundTo(x, k) = round(x / k) * k.
}}}
> Rounding to N decimal places is just a case of using k=1/10^N^. If you
> allow k to be specified directly, then you can round to arbitrary
> steps (e.g. k=5 would round to the nearest multiple of 5, etc).

I was just looking at the function round() in R which rounds to decimal
places. Generalised rounding makes more sense.

>
> However: there's a slight problem with doing it that way: 0.1 isn't
> exactly representable in binary, so e.g. x/0.1 isn't equal to x*10; it
> would be more accurate to use:
{{{
         roundTo(x, k) = round(x * k) / k
}}}
> where k is the reciprocal of the step, so k=10^N^ to round to N decimal
> places (or k=2 to round to 1/2).

>
> Unless someone has a better idea, I plan to change the round() function
so that the second argument is the step value, and add an roundi() (round-
inverse) function where the second argument is the reciprocal of the step
value (to avoid the rounding error when using a step of 10^-N^).

Sounds good to me.

--
Ticket URL: <http://trac.osgeo.org/grass/ticket/1976#comment:5&gt;
GRASS GIS <http://grass.osgeo.org>

On Tue, May 21, 2013 at 5:00 PM, GRASS GIS <trac@osgeo.org> wrote:

#1976: r.mapcalc: Allow rounding of floating numbers

-------------------------+--------------------------------------------------
Reporter: pvanbosgeo | Owner: grass-dev@…
     Type: enhancement | Status: new
Priority: normal | Milestone: 7.0.0
Component: Raster | Version: unspecified
Keywords: r.mapcalc | Platform: Unspecified
      Cpu: Unspecified |

-------------------------+--------------------------------------------------

Comment(by mmetz):

Replying to [comment:4 glynn]:
> Replying to [comment:2 mmetz]:
>
> > The output type of round() is now the same like the input type.
>
> This changes long-standing behaviour in a way which could break scripts.

OK. Restoring the original behaviour for round(x) is easy. But it would be
nice to have a round(x, y) function that preserves the data type of x in
order to have a possibility to avoid integer overflow.

I am not sure I understand the solution proposed by Glyn, but does it mean
we are back at the results of round always be an integer? That would be
disappointing imho. Preserving the data type does make so much more sense.

>
> > Rounding to a given number of decimal places is supported with
round(x, y)
>
> From the mailing list discussion ...
>
> Rounding to a given number of decimals is unnecessarily limiting. The
algorithm for generalised rounding is essentially:
>
{{{
         roundTo(x, k) = round(x / k) * k.
}}}
> Rounding to N decimal places is just a case of using k=1/10^N^. If you
> allow k to be specified directly, then you can round to arbitrary
> steps (e.g. k=5 would round to the nearest multiple of 5, etc).

I was just looking at the function round() in R which rounds to decimal
places. Generalised rounding makes more sense.

>
> However: there's a slight problem with doing it that way: 0.1 isn't
> exactly representable in binary, so e.g. x/0.1 isn't equal to x*10; it
> would be more accurate to use:
{{{
         roundTo(x, k) = round(x * k) / k
}}}
> where k is the reciprocal of the step, so k=10^N^ to round to N decimal
> places (or k=2 to round to 1/2).

>
> Unless someone has a better idea, I plan to change the round() function
so that the second argument is the step value, and add an roundi() (round-
inverse) function where the second argument is the reciprocal of the step
value (to avoid the rounding error when using a step of 10^-N^).

Sounds good to me.

--
Ticket URL: <http://trac.osgeo.org/grass/ticket/1976#comment:5&gt;
GRASS GIS <http://grass.osgeo.org>

Paulo van Breugel wrote:

> > > The output type of round() is now the same like the input type.
> >
> > This changes long-standing behaviour in a way which could break scripts.
>
> OK. Restoring the original behaviour for round(x) is easy. But it would be
> nice to have a round(x, y) function that preserves the data type of x in
> order to have a possibility to avoid integer overflow.

I am not sure I understand the solution proposed by Glyn, but does it mean
we are back at the results of round always be an integer? That would be
disappointing imho. Preserving the data type does make so much more sense.

No. With one argument, the result will be an integer, so the
long-standing behaviour will be preserved. If a second argument is
given, the result will have the same type as the second argument, so
e.g. round(x, 1.0) will return a double.

--
Glynn Clements <glynn@gclements.plus.com>

#1976: r.mapcalc: Allow rounding of floating numbers
-------------------------+--------------------------------------------------
Reporter: pvanbosgeo | Owner: grass-dev@…
     Type: enhancement | Status: new
Priority: normal | Milestone: 7.0.0
Component: Raster | Version: unspecified
Keywords: r.mapcalc | Platform: Unspecified
      Cpu: Unspecified |
-------------------------+--------------------------------------------------

Comment(by glynn):

Replying to [comment:4 glynn]:

> Unless someone has a better idea, I plan to change the round() function
so that the second argument is the step value,

Done in r56365. An optional third argument is the start value, so e.g.
round(x,1.0,0.5) will round to the nearest something-point-five value.

> and add an roundi() (round-inverse) function where the second argument
is the reciprocal of the step value (to avoid the rounding error when
using a step of 10^-N^).

I haven't bothered with this. If the step value can't be represented
exactly, then in the general case, neither can the rounded value. If it's
desired, it would be better to clone xround.c and modify the i_round()
function (swap the multiplication and division) than to try to get yet
another case into that file.

Rounding to a given number of decimal places (as opposed to rounding to a
multiple of 10^-N^) is something which really needs to be done during
conversion to a string. Attempting to round a floating-point value to a
given number of decimal places will inevitably add rounding errors which
may be visible if the value is subsequently converted to a string using
sufficient precision.

--
Ticket URL: <http://trac.osgeo.org/grass/ticket/1976#comment:6&gt;
GRASS GIS <http://grass.osgeo.org>

#1976: r.mapcalc: Allow rounding of floating numbers
-------------------------+--------------------------------------------------
Reporter: pvanbosgeo | Owner: grass-dev@…
     Type: enhancement | Status: new
Priority: normal | Milestone: 7.0.0
Component: Raster | Version: unspecified
Keywords: r.mapcalc | Platform: Unspecified
      Cpu: Unspecified |
-------------------------+--------------------------------------------------

Comment(by pvanbosgeo):

Hi Glynn, this looks and works great. Initially I wasn't really clear
about the implementation, but the round(x,y) seems to do exactly what I
hoped for (including rounding numbers outside the integer range), great!

The explanations for round(x,y) and round(x,y,z) in the r.mapcalc help
file are perhaps not immediately clear (but that might be me). Maybe it is
possible to get some examples in the help file? In any case, great work.

--
Ticket URL: <http://trac.osgeo.org/grass/ticket/1976#comment:7&gt;
GRASS GIS <http://grass.osgeo.org>

#1976: r.mapcalc: Allow rounding of floating numbers
-------------------------+--------------------------------------------------
Reporter: pvanbosgeo | Owner: grass-dev@…
     Type: enhancement | Status: new
Priority: normal | Milestone: 7.0.0
Component: Raster | Version: unspecified
Keywords: r.mapcalc | Platform: Unspecified
      Cpu: Unspecified |
-------------------------+--------------------------------------------------

Comment(by neteler):

Replying to [comment:7 pvanbosgeo]:
> The explanations for round(x,y) and round(x,y,z) in the r.mapcalc help
file are perhaps not immediately clear (but that might be me). Maybe it is
possible to get some examples in the help file?

An example for each round(x,y) and round(x,y,z) would be appreciated (if
here, I am
happy to add them the manual).

E.g. convert degree Celsius map/floating point to 10*degC (like BIOCLIM)
as integer?

--
Ticket URL: <http://trac.osgeo.org/grass/ticket/1976#comment:8&gt;
GRASS GIS <http://grass.osgeo.org>