It seems obvious, that b-a >0, and therefore (b-a)*v >=0, so that we don't need to check: if (x<a) return a;
The property b - a > 0
is correct in IEEE 754 but I wouldn't say that it is obvious. At the time of floating-point standardization, Kahan fought for this property resulting from “gradual underflow” to be true. Other proposals did not have subnormal numbers and did not make it true. You could have b > a
and b - a == 0
in these other proposals, for instance by taking a
the smallest positive number and b
its successor.
Even without gradual underflow, on a system that mis-implements IEEE 754 by flushing subnormals to zero, b > a
implies b - a >= 0
, so that there is no need to worry about x
being below a
.
But is this also redundant? if (x>=b) return std::nextafter(b,a);
This test is not redundant even in IEEE 754. For an example take b
to be the successor of a
. For all values of v
above 0.5
, in the default round-to-nearest mode, the result of a + (b-a)*v
is b
, which you were trying to avoid.
My examples are built with unusual values for a
and b
because that saves me from writing a program to find counter-examples through brute-force, but do not assume that other, more likely, pairs of values for a
and b
do not exhibit the problem. If I was looking for additional counter-examples, I would in particular look for pairs of values for which the floating-point subtraction b - a
rounds up.
EDIT: Oh alright here is another counter-example:
Take a
to be the successor of the successor of -1.0
(that is, in double-precision, using C99's hexadecimal notation, -0x1.ffffffffffffep-1
) and b
to be 3.0
. Then b - a
rounds up to 4.0, and taking v
to be the predecessor of 1.0
, a + (b - a) * v
rounds up to 3.0
.
The floating-point subtraction b - a
rounding up is not necessary for some values of a
and b
making a counter-example, as shown here: taking a
as the successor of 1.0
and b
as 2.0
also works.