8
$\begingroup$

I want to get the four corners of a Cuboid. This usually works:

CoordinateBoundingBoxArray[{-{1., 1.}, {1., 1.}}, Into@1]

gives

{{{-1., -1.}, {-1., 1.}}, {{1., -1.}, {1., 1.}}}

as expected.

The expressions CoordinateBoundingBoxArray[{-{1., 0.40}, {1., 0.40}}, Into@1] and CoordinateBoundingBoxArray[{-{1., 0.42}, {1., 0.42}}, Into@1] evaluate to a 2x2 matrix of 2d coordinates as well, as I would expect. But for some reason

CoordinateBoundingBoxArray[{-{1., 0.41}, {1., 0.41}}, Into@1]

gives

{{{-1., -0.41}}, {{1., -0.41}}}

I am confident that this is not the way this should work.

$\endgroup$
  • 1
    $\begingroup$ If you're confident, the thing I do is to report it to WRI, not to Mma.SE. $\endgroup$ – Michael E2 Jan 15 '17 at 14:32
  • 1
    $\begingroup$ I am feeling quite ignorant right now; what is Into? $\endgroup$ – Mr.Wizard Jan 15 '17 at 16:13
  • 1
    $\begingroup$ CoordinateBoundingBoxArray[{min,max},Into[n]] divides into n equal steps in each dimension. reference.wolfram.com/language/ref/… $\endgroup$ – Quantum_Oli Jan 15 '17 at 16:41
  • 2
    $\begingroup$ @Mr.Wizard. See CoordinateBoundsArray. It is otherwise undocumented AFAIK $\endgroup$ – m_goldberg Jan 15 '17 at 18:22
  • 1
    $\begingroup$ @J.M. At this point my early senility isn't even an open secret, it's just plain out in the open. I'll keep plugging away until I forget how to sign in or what @@ does. $\endgroup$ – Mr.Wizard Jan 16 '17 at 4:14
9
$\begingroup$

The problem arises from rounding in machine precision. For instance the second-coordinate interval {-0.41, 0.41} leads to this edge case, a one ulp error:

SetAccuracy[1/0.82*0.82, Infinity]
(*  9007199254740991/9007199254740992  *)

It turns out this affects how the range is computed, because Floor is used on it and it returns 0 instead of 1.

One workaround is to avoid machine precision, using either exact numbers or arbitrary precision. Another workaround is to create a Floor that applies a machine-precision fudge factor, in the way that Equal has the fudge factor Internal`$EqualTolerance.

First way:

ClearAll[cbba];
cbba = SetPrecision[
    CoordinateBoundingBoxArray[
     # /. x_?Developer`MachineRealQ :> SetPrecision[x, $MachinePrecision], ##2],
     Precision[#]] &; 

Second way, mostly just as evidence that Floor is implicated:

ClearAll[cbba];
cbba[v_, rest__] /; Precision[v] === MachinePrecision :=
  Internal`InheritedBlock[{Floor},
   Unprotect@Floor;
   Floor[x_] /; ! TrueQ[$in] := Block[{$in = True},   (* Villegas-Gayley *)
     Floor[x + 10^Internal`$EqualTolerance $MachineEpsilon Abs[x]]];
   Protect@Floor;
   CoordinateBoundingBoxArray[v, rest]
   ];

Or even more simply:

ClearAll[cbba];
cbba[args___] /; FreeQ[{args}, Floor] :=
  Block[{Floor = Round},
   CoordinateBoundingBoxArray[args]
   ];

I can't find a way in which this substitution of Round for Floor causes a problem in CoordinateBoundingBoxArray, but I suppose it might.

Test:

cbba[{-{1., 0.41}, {1., 0.41}}, Into@1]
(*  {{{-1., -0.41}, {-1., 0.41}}, {{1., -0.41}, {1., 0.41}}}  *)
| improve this answer | |
$\endgroup$
  • $\begingroup$ Awesome research here, thank you! $\endgroup$ – masterxilo Jan 15 '17 at 21:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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