[GRASS-dev] r.mapcal rand() strangeness
Glynn Clements
glynn at gclements.plus.com
Mon Feb 25 01:02:31 EST 2008
Maciej Sieczka wrote:
> >>>> $ r.mapcalc 'map=rand(-2147483648,2147483647)'
>
> >>> res[i] = (lo == hi) ? lo : lo + x % (hi - lo);
> >>>
> >>> The expression (hi - lo) is overflowing the range of a signed integer.
>
> > Are all GRASS modules limited to Int32, ie. -2147483648 to 214748364? I
> > can see r.mapcalc silently forces 214748364 for anything bigger...
>
> This is not entirely correct. More findings:
>
>
>
> The following silently forces 2147483647 on anything bigger:
>
> $ r.mapcalc 'map=9999999999999999999'
> $ r.info -rt map
> min=2147483647
> max=2147483647
> datatype=CELL
That is down to the atoi() function used to parse integers.
I could change it to use strtol(), which sets errno to ERANGE on
overflow.
> This one too, though I'm trying to enforce double:
>
> $ r.mapcalc 'map=double(9999999999999999999)'
> $ r.info -rt map
> min=2147483647.000000
> max=2147483647.000000
> datatype=DCELL
The over-large integer constant is truncated by atoi(), then the
truncated constant is converted to a double.
> Trying the same by adding a decimal place separator, results in
> something bizzare to me:
>
> $ r.mapcalc 'map=99999999999999999999999.0'
> $ r.info -rt map
> min=99999999999999991611392.000000
> max=99999999999999991611392.000000
> datatype=DCELL
The presence of a dot causes the value to be parsed as a
floating-point value using atof().
A double has a precision of ~16 decimal digits, which matches what you
see above.
> $ r.mapcalc 'map=9999999999999999999.0'
> $ r.info -rt map
> min=10000000000000000000.000000
> max=10000000000000000000.000000
> datatype=DCELL
Ditto.
> At the edges of Float32 range other strange things happen:
>
> Shouldn't the below be rather
> min,max=340282000000000000000000000000000000000.0?:
>
> $ r.mapcalc 'map=3.40282e38'
> $ r.info -rt map
> min=340282000000000014192072600942972764160.000000
> max=340282000000000014192072600942972764160.000000
> datatype=DCELL
>
> The the one below should be -340282000000000000000000000000000000000.0 I
> guess:
>
> $ r.mapcalc 'map=-3.40282e38'
> $ r.info -rt map
> min=-340282000000000014192072600942972764160.000000
> max=-340282000000000014192072600942972764160.000000
> datatype=DCEL
Again, you're running into the limits of the precision of the double
type.
Also, bear in mind that the conversion from a binary FP value to a
string of decimal digits is being performed by r.info; r.mapcalc has
no control over that part of the process.
Essentially, the %f format behaves badly for large numbers. If you use
%e or %g, it will use exponential notation, with a specific relative
precision rather than a specific absolute precision.
> At bigger Float64 values r.mapcalc crashes:
>
> $r.mapcalc 'map=3.40282e110'
>
> *** stack smashing detected ***: r.mapcalc terminated
> Aborted (core dumped)
>
> $ r.mapcalc 'map=1.79769e+308'
> 100%
> *** stack smashing detected ***: r.mapcalc terminated
> Aborted (core dumped
Right. It's sprintf()ing the value into a 64-byte buffer using %f.
I'll change it to use %g:
--- raster/r.mapcalc/expression.c (revision 30318)
+++ raster/r.mapcalc/expression.c (working copy)
@@ -312,7 +312,7 @@
if (e->res_type == CELL_TYPE)
sprintf(buff, "%d", e->data.con.ival);
else
- sprintf(buff, "%f", e->data.con.fval);
+ sprintf(buff, "%.8g", e->data.con.fval);
return strdup(buff);
}
--
Glynn Clements <glynn at gclements.plus.com>
More information about the grass-dev
mailing list