It looks like you have to program it yourself. At a boundary x == 2.^n
, the distance to the next machine real is either x * $MachineEpsilon
or x * $MachineEpsilon / 2
. The documentation for MantissaExponent
ambiguously states that the mantissa will be "between $1/b$ and $1$". It seems be the case that $1/b \le \mathtt{mantissa} < 1$.
nextafter[0., y_] := Sign[y] $MinMachineNumber;
nextafter[x_, y_] /; Precision[x] == MachinePrecision :=
With[{mantexp = MantissaExponent[x, 2]},
Piecewise[{
{First[mantexp] + Sign[y] $MachineEpsilon/4.,
Sign[x] Sign[y] < 0 && First[mantexp] == 1./2.}},
First[mantexp] + Sign[y] $MachineEpsilon/2.] * 2.^Last[mantexp]
];
A couple of tests:
Block[{x = 128. - 128*$MachineEpsilon/2},
Print @ RealDigits[{nextafter[x, -1], x, nextafter[x, 1]}, 2];
Differences[{nextafter[x, -1], x, nextafter[x, 1]}]
]
(*
{{{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0}, 7},
{{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1}, 7},
{{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0}, 8}}
{1.42109*10^-14, 1.42109*10^-14}
*)
Block[{x = 128.},
Print @ RealDigits[{nextafter[x, -1], x, nextafter[x, 1]}, 2];
Differences[{nextafter[x, -1], x, nextafter[x, 1]}]
]
(*
{{{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1}, 7},
{{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0}, 8},
{{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1}, 8}}
{1.42109*10^-14, 2.84217*10^-14}
*)
To deal with subnormal numbers, one has to use Compile
, ASAIK.
nextafter = With[{min = $MinMachineNumber},
Compile[{x, y},
Block[{e, laste},
If[x == 0,
Sign[y] min/2.^52,
If[x < 1,
e = 2.^(Ceiling @ Log2 @ Abs[x] + 52);
laste = e*2^-52,
laste = e = 2.^Ceiling[Log2 @ Abs[x] - 52]];
While[x + Sign[y] e != x,
laste = e; e = e/2.];
x + Sign[y] laste]],
RuntimeOptions -> {"CompareWithTolerance" -> False}]]
You'll get an error on $MaxMachineNumber
depending on the direction. I hope I haven't tripped over any other boundary traps.
x + Sign[y] $MachineEpsilon
should work, yes? $\endgroup$$MachineEpsilon
is the smallest floating-point number such that1 + $MachineEpsilon != 1
, but for certain values, the distance to the next representable float is much smaller. For example, consider the distance between1.0e-300
and the next number up. $\endgroup$Ulp
et al. in the Computer Arithmetic package... $\endgroup$