6
$\begingroup$

I understand that Round give the nearest even integer for cases where the number is between two integers, i.e. Round[2.5] = 2 and Round[3.5] = 4 (see this question). This carries over to rounding to non-integers that are not orders of magnitude as well:

Round[1.3, 0.2] = 1.2
Round[1.5, 0.2] = 1.6

Round[Range[1, 2, 0.1], 0.2] = {1., 1.2, 1.2, 1.2, 1.4, 1.6, 1.6, 1.6, 1.8, 1.8, 2.}

However, the Floor function seems to behave oddly in a similar situation:

Floor[Range[1, 2, 0.1], 0.2] = {1., 1., 1., 1.2, 1.2, 1.4, 1.6, 1.6, 1.8, 1.8, 2.}

but Ceiling does what I would expect it to do:

Ceiling[Range[1, 2, 0.1], 0.2] = {1., 1.2, 1.2, 1.4, 1.4, 1.6, 1.6, 1.8, 1.8, 2., 2.}

My question therefore is why does Floor[1.2, 0.2] = 1.? I understand the behavior of Round for this test set, but I do not understand Floor. Then, with Floor giving something I don't expect, I am very surprised that Ceiling gives exactly what I would expect.


Background for my Problem

I have a regular array of 2D data ({x, y, probability}) that I want to bin by summing, so Round doesn't work because different numbers of elements are included in consecutive bins because it rounds to evens, but I expected Floor to work.

gathered = GatherBy[predictedProbabilityDist,
   { Round[#[[1]], newGridSpacing],
     Round[#[[2]], newGridSpacing] } &];

versus

gathered = GatherBy[predictedProbabilityDist,
   { Floor[#[[1]], newGridSpacing],
     Floor[#[[2]], newGridSpacing] } &];

or the nearly equivalent expression with Ceiling, followed by something like

predictedProbDist = Map[
   { Min[#[[All, 1]]],
     Min[#[[All, 2]]],
     Total[#[[All, 3]]] } &,
   gathered];

Based on the testing with the simple Range above, it looks like I need to use Ceiling, but I don't know why for sure that is the case.

$\endgroup$
9
$\begingroup$

Welcome in the amazing world of machine precision arithmetic!

If you examine the binary representation of both numbers you see the following:

RealDigits[1.2, 2]
(* {{1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
   1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 
  0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1}, 1} *)

RealDigits[.2, 2]
(* {{1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
   1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 
  0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0}, -2} *)

The .2 part is represented by an infinite series of 0,0,1,1,.. that necessarily has to be truncated to fit into the allocated space. Since the fractional part has slightly more space in the representation of 0.2 than in 1.2 (because of the missing integer part) it is therefore slightly larger than the fractional part of 1.2 and therefore 1.2 is seen as smaller than a multiple of 0.2. Compare also

Floor[1.20000000000000001, .2]
(* 1. *)

and

Floor[1.200000000000001, .2]
(* 1.2 *).

Using arbitrary precison arithmetic you can make the representation of the bit series in 1.2 longer than in 0.2 and the result is now:

Floor[1.2`200, .2`10]
(* 1.200000000 *)
$\endgroup$
3
$\begingroup$

Sjoerd C. de Vries correctly diagnoses the problem and provides a resolution. An alternative is to work in exact numbers and convert to floating point at the end.

Floor[Range[1, 2, 1/10], 2/10]
(* Output:  {1, 1, 6/5, 6/5, 7/5, 7/5, 8/5, 8/5, 9/5, 9/5, 2} *)

N[Floor[Range[1, 2, 1/10], 2/10]]
(* Output:  {1., 1., 1.2, 1.2, 1.4, 1.4, 1.6, 1.6, 1.8, 1.8, 2.} *)

Note that you may still have trouble with your binning at one or both endpoints of your range.
Alternatively, since you have a (full?) regular grid, you may be happier using Partition[] since it does what you want structurally instead of by means of comparisons. (The following is in no sense optimal. For instance, if your {x,y,prob} triples are sorted in y and then by x, partitioning [[All,3]] would give you the dataTable directly.)

inData = (* linear array of {x,y,probability} triples *)
((intermediateData[#[[1]], #[[2]]] = #[[3]]) &) /@ inData
dataTable = Table[intermediateData[x,y],{x,(* min x *), (* max x *)}, {y, (* min y *), (* max y *)}]  (* the minimum and maximum values can be extracted from inData or just supplied by hand. *)
Total[Flatten[#]] & /@ # & /@ Partition[dataTable, {(* columns per grid *), (* rows per grid *)}]

for instance (my random numbers might not match your random numbers), with a 10x10 initial grid and 5x5 cells...

inData = Flatten[Table[{x, y, Random[]}, {x, 1, 10}, {y, 1, 10}], 1]
((intermediateData[#[[1]], #[[2]]] = #[[3]]) &) /@ inData
dataTable = Table[intermediateData[x, y], {x, 1, 10}, {y, 1, 10}]
Total[Flatten[#]] & /@ # & /@ Partition[dataTable, {5, 5}]
(* last Output:  {{12.0149, 15.3413}, {12.7288, 11.8542}} *)

Partition[] will provide partial cells at the upper ends of the x and y ranges if the cell size does not exactly divide the number of rows or columns.

$\endgroup$
  • $\begingroup$ And today I learn about the function BlockMap[], new to Mathematica 10.2. reference.wolfram.com/language/ref/BlockMap.html which would act on the dataTable directly. $\endgroup$ – Eric Towers Jul 14 '15 at 20:02
  • $\begingroup$ Unfortunately, the data I'm working with is imported from something else, so working in exact numbers is not a good solution at this point. I had thought about Partition as well, but hadn't wanted to make it a 2D array because I don't a priori know the dimensions, but I could figure them out to use Partition or BlockMap. I think instead of your sorting solution, I would use Sort, though. Your method is nice in that it could be Compiled, I believe. $\endgroup$ – ThomasH Jul 20 '15 at 21:47

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.