7
$\begingroup$

When creating an Interval object, the end points are automatically widened when an end point is an inexact number. For instance:

Interval[{.2`2, 1.2`2}] //InputForm

Interval[{0.19609375`1.9914337561691888, 1.215625`2.0056183769581764}]

How can I create an Interval object and avoid this automatic widening? That is, can a function PreciseInterval be defined such that:

PreciseInterval[{.2`2, 1.2`2}] //FullForm

gives

Interval[{.2`2, 1.2`2}]
$\endgroup$
  • 3
    $\begingroup$ So when are you going to tell us the answer ...? :-) $\endgroup$ – user1066 Sep 23 '18 at 18:19
4
$\begingroup$

I will present two solutions, a hard (and not quite complete) one and an easy (but undocumented) one. First, the hard one.

  1. Use Interval[real] to determine the default widening.

Consider:

r = 1.23`2;
(int = Interval[r]) //InputForm

Interval[{1.214375`1.9944477065044612, 1.245625`2.0054822046051672}]

Note how an Interval object is created that includes the input r. Now, for the key observation: Repeat the above procedure with the end points:

{left, right} = First @ int;
Interval[left] //InputForm
Interval[right] //InputForm

Interval[{1.19875`1.988823508739323, 1.23`2.}]

Interval[{1.23`2., 1.26125`2.0108960678055694}]

Notice how the original input r reappears. So, the basic algorithm to create the desired output is:

PreciseInterval[{l_, r_}] := Interval[{Interval[l][[1,2]], Interval[r][[1,1]]}]

For example:

PreciseInterval[{1.2`3, 3.3`5}] //InputForm

Interval[{1.2`3., 3.3`5.}]

The only problem is that this algorithm doesn't work when the end point generated by Interval is an arbitrary precision 0. For example:

Interval[.1`.2] //InputForm

Interval[{0``1.2, 0.225`0.5521825181113625}]

In this case, PreciseInterval gives:

PreciseInterval[{-1, .1`.2}] //InputForm

Interval[{-1, 0.1000000000000000056`0.4259697322722812}]

which isn't bad. It is possible to do slightly better, but instead, let's turn to the easy method.

  1. The easy (but undocumented) option

The issue would be moot if one could figure out a way to create an "evaluated" Interval object without having the Interval object actually evaluate. And, there is a way to do this! The needed function is System`Private`HoldSetValid. The function System`Private`HoldSetValid tells Mathematica that its argument has already been evaluated, so Mathematica need not try to evaluate it again. Using this function:

Clear[PreciseInterval]
PreciseInterval[{a_, b_}] := System`Private`HoldSetValid[Interval[{a, b}]]

Let's check the previous example:

PreciseInterval[{-1, .1`.2}]//InputForm

Interval[{-1, 0.1`0.2}]

$\endgroup$
  • 1
    $\begingroup$ HoldSetValid[] is neat! On the default widening for floats: this is basically determining the nearest floating point numbers that straddle the input float; I exploited that property for this answer. $\endgroup$ – J. M. is in limbo Sep 25 '18 at 3:48
  • $\begingroup$ I'd seen SetValid, ValidQ, and friends before but never realized they were this powerful. That is very useful. How old are they? (10.0 or earlier?) $\endgroup$ – b3m2a1 Sep 25 '18 at 22:13
  • $\begingroup$ What functions make use of Valid? I've been struggling to find other objects for which this works. $\endgroup$ – b3m2a1 Sep 26 '18 at 8:22
  • $\begingroup$ It is worth to include this useful function into this answer: mathematica.stackexchange.com/a/139974/280 $\endgroup$ – Alexey Popkov Oct 22 '18 at 7:14
3
$\begingroup$

Given the discrete nature of numbers, there are probably edge cases where the problem is unsolvable. Here's a start:

ClearAll[preciseInterval];
preciseInterval[int_] := Module[{obj, res, x, y, px, py},
   obj[x_?NumericQ, y_?NumericQ, px_?NumericQ, py_?NumericQ, wp_] := 
    Block[{(*$MaxPrecision,$MinPrecision*)},
     SetPrecision[
      Flatten@{#, Precision /@ #} &@
       First@Interval[{SetPrecision[x, px], SetPrecision[y, py]}],
      wp]
     ];
   With[{prec = 2 Max[Precision[int], $MachinePrecision]},
    res = FindRoot[
      obj[x, y, px, py, prec] == 
       SetPrecision[Flatten@{int, Precision /@ int}, prec],
      {{x, First@int, (1 + 5 10^-Precision[int]) First@int},
       {y, Last@int, (1 + 5 10^-Precision[int]) Last@int},
       {px, Precision@First@int, (1 + 5 10^-Precision[int]) Precision@First@int},
       {py, Precision@Last@int, (1 + 5 10^-Precision[int]) Precision@Last@int}}, 
      WorkingPrecision -> prec
      ]
    ];
   (Interval[Quiet@{SetPrecision[x, px], SetPrecision[y, py]}] /. res) /; 
    FreeQ[res, FindRoot]
   ];

preciseInterval[{0.2`2., 1.2`2.}] // InputForm
(*  Interval[{0.2`2., 1.2`2.}]  *)

There are four parameters to solve for, the two numbers and their precisions. The above does not work for infinite-precision numbers nor, regrettably, zero. I wasn't sure whether FindRoot uses MaxPrecision, $MinPrecision and I would need to protect the obj[] function. Apparently it's unnecessary to block them, but I left it in, just in case.

$\endgroup$
  • $\begingroup$ Thanks, this is a nice answer. $\endgroup$ – Carl Woll Sep 25 '18 at 3:33
2
$\begingroup$

I'm not sure what you are trying to achieve here - this doesn't look very useful to me. Mathematica gives us two ways of expressing the uncertainty in a number, either by giving its precision or by giving an interval in which it lies: you are trying to use both at once.

By specifying a precision for the limits of the interval, you are saying that you are uncertain about exactly what those limits are. From the help for Interval:

In operations on intervals that involve approximate numbers, the Wolfram Language always rounds lower limits down and upper limits up.

In this way, Mathematica attempts to explain all the uncertainty in the interval, rather than the precision of the limits.

$\endgroup$

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.