The code continues im parallel with the case for ulps
just larger than 1.0
, but with a twist.
To investigate values immediately less than 1.0
,
we look at the expression
1−(1−ϵ)
where ϵ is a tiny value, not necessarily a whole
number of ulps of numbers less than one.
The wrinkle is that we must take care not to lose a digit off the right hand side by consuming a digit in the one's place.
The idiom for computing 1−w when w<1is to use two operations 1/2+(1/2−w) avoiding use of the one's palce in any subexpression. The formula above expands to 1/2+(1/2−(1/2+(1/2−ϵ))) which you will see in four lines of the code below.
In the loop, y
is half a whole number of ulps plus
a small nudge. When subtracted from ONE_HALF
the result
rounds to 1/2−δ, where Greek delta is a whole number of
ulps of values just less than 1.0
.
The combined expression ONE_HALF + (ONE_HALF - y)
is
exactly 1−δ.
We then apply the same tecchnique to compute
1−(1−δ)
to isolate δ, iterating until it is down to
a single ulp.
This binary diagram starts with 3 ulps of values just less
than 1.0
. It illustrates the computation of
1−δ
Next, we subtract from 1.0
in two steps to isolate
the difference.
As in the parallel case, the loop terminates when the halved value
falls off to zero when rounded or when u
is pinned.
while True:
u = x
y = ONE_HALF * u + THIRTY_TWO * u * u
y = ONE_HALF - y
x = ONE_HALF + y
y = ONE_HALF - x
x = ONE_HALF + y
if u <= x or x <= ZERO: break
return u
Exercise: Complete the binary case
started in the diagram above, assuming rounded arithmetic,
to see that it results in
one ulp of 1.0
.
Exercise: The decimal diagram below starts the iteration in 6-digit rounded decimal arithmetic.
Continue the loop to find one ulp of 1.0
.
Exercise: Complete the computation in 6-digit chopped octal arithmetic.