[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Zeroes and infinities



Sorry folks, I was busy and was not able to answer...

There are some general principles used in the standard that cover
pretty well all cases for just about any real valued function of a
real variable, at least in the default round-to-nearest mode.
These principles require NaN^0 return NaN.  In some cases, there
is a feeling that an exception to these principles is more
convenient or more intelligible.  0.0^0.0 = 1.0 is one of these
cases.

What exactly yields the example of one case 0^0 == 1 in regard of
the question what NaN^0 should be ? And 0^0 == 1 is not just a
"feeling", it fulfills the valuable binominal theorem in case of 0.

The reason I became involved in these discussions was that I have
strong objections to the the pow function as presented in David
James' tables.  The version presented in his table is modelled on
the pow function in the C99 standard.  My objection is that this
function is a mish-mash of two functions that should be implemented
separately.

Explicitly, there should be two functions with signatures powI:
[-infty, +infty]x{the integers} -> [-infty, +infty] and powR: [0,
+infty]x[-infty, +infty] -> [0, +infty].  powI just packages the
usual integer exponentiation for which x^0 = 1.0 for all x in
[-infty,infty].  This is the version relevent to the binomial
theorem and powI(0.0, 0)  = 1.0.  powF is different, as there is no
limit as (x,y) approaches (0.0, 0.0).  Paths for x and y can be
found yielding any limit in [0, +infty].  This is the function that
I was referring to above and it is the proposal that powF(0.0, 0.0)
= 1.0 that is generating some argument.  My views on this particular
case are not strong either way.  As for powI(NaN, 0) and powF(NaN,
0.0), I feel strongly that both should return NaN, not 1.0.

So do I.
I wanted to comment on your former proposal, but forgot its contents.
So I couldn't imagine what exactly are you referring with the example
of 0^0 = 1 and was a bit helpless.
Now, 0.0^0.0 is for me still a bit different. It isn't the number
0.0^0.0 which can yield another limit than 1, what happens is that
one of the functions f(x) or g(x) with f(x)^g(x) has another
convergence speed to 0.0 and therefore yields values other than 1.

Yes, with limited precision it will be inevitably rounded to
0.0^0.0, but what should the computer do ? The user should be
warned by underflow condition to resolve this particular problem,
but more cannot be done.

But NaN^0 is totally different because it assumes that the
result of an operation must be a number which is IMO bogus. It
handles anomalous conditions like an ostrich sticking his head
into sand by patching and hiding senseless results. One don't
see a problem therefore there is no problem.

Your idea of using two functions is ok for me, but the user....
He will very likely ignore the powI function.

But I am not talking about Infinity. I am talking about the result
of the *number* one divided by *number* zero. It seems to me that
you have substituted automatically "infinity" for 1/0 when I haven't
talked about the number "infinity" at all (I have said that 1/0 isn't
an infinity).
But IEEE 754 does, because in many cases it can be used to produce
useful results, for example the straight-forward, fast and highly
accurate evaluation of continued fractions,

Why highly accurate ?
The continued fraction should be (b[0],b[1],....);

If the denominator of a continued fraction containing the constant
b[i] equals 0.0, all former calculations of higher b[i]s will be lost
and the result will be equivalent to (b0,b1,....,b[i-2]). It will
simulate a result of greater accuracy when in fact it stagnates.
If the programmer calculate them with ever increasing precision, he
will probably encounter strange discontinuties (when the denomiator
hits zero).

and it allows overflow and divide by zero to be indicated without
having to check flags or causing a core dump.

I am a programmer. I have never ever checked if the result of a
division is +-Infinity. In fact, I always avoid infinities,
division by zero and NaNs like the pest. The reason is easy: You
can't do meaningful calculations with these values. You don't
want to propagate it, so it comes down to write handler routines
for two/three different states to resolve the result.
I always go the other way, ask myself if
my routine can cause this result and rewrite it so the case
could not happen (substituition values, regrouping etc.).
That is easier and much more stable than trying to test and
handle these values.

Generally, programmers don't attempt to calculate 1.0/0.0 out of the
blue.  Usually it sneaks up on them as a result of rounding error or
underflow.  If a function has a region where it is identically zero,
then the programmer has to treat this carefully and 1.0/0.0 = infty
is unlikely to be the correct response.  But this is just part of
understanding the problem at hand.

And this is the reason I prefer trapping instead of substituition
values.

The IEEE standard takes the approach that infinity is the result
of a limiting operation and the rules follow from the algebra of
limit theorem for real-valued functions of several real variables.
.
Ah, you are substituting the numbers by limit operations, so in your
opinion IEEE 754 means effectively[...]:
And that f(number) must therefore return NaN if the number has no
single limit ? Is that correct ?
Yes.  But zero is a special case. 

I just wanted to point out this discrepancy.

Should you ever take a course in advanced theory of integration
(usually called measure theory), among the first things
Well, I haven't taken a course in measure theory yet, probably
because I am too dumb for such complicated things,
I apologise for causing offence.  It is so difficult to get the tone
exactly right in these sort of forums.  All I really meant was that
if you need to study certain areas, you will find that infinity is
invoked, and in addition, the rules of infinity arithmetic differ
from application to application.

Accepted.

One thing -- in school I learned that numbers are
something like "fields", containing an inverse element for
multiplication and therefore the operation of division. Are these new
extended real numbers fields, too ? Is therefore
the operation "/" comparable with the operation division in a field ?
Where an arithmetic is defined for infinity, the attempt is usually
made to preserve as many of the usual rules as possible.  Most
commonly what is lost is closure.  i.e. not all combinations of
operands produce a defined result.  IEEE arithmetic uses the algebra
of limits theorem to guide the manner in which the arithmetic is
extended from the reals to +/-infty.  Hence 0/0 is not defined,
since for any y in [-infty, +infty], we can find functions f(x) and
g(x) that tend to zero as x tends to 1 say, such that f(x)/g(x)
tends to y.  In the example from integration theory, unlike IEEE
arithmetic, 0*infty=0 and x*0=0 for all x in [-infty,+infty].

I think you have understand my hint ;-). Concerning 0/0 I don't see it
as a NaN, but as an indeterminate expression which has a limit value
and can therefore be fixed (like the sinc function).

While FP numbers are crude approximations of the fields R and C
(not obeying the distributive law), they are intended to model them,
right ?
Not exactly. FP numbers are exact representations of real numbers.
It is the floating point operations +, -, / and * that are
approximations of their counterparts from the real number system,
just as any floating point evaluation of a function is an
approximation of the actual real value.

No, I didn't mean that FP numbers should model crude approximations of R
and C or model an interval, but that the "crude approximation" is
inherently a result of limited precision. As you say, the FP numbers
should model R and C and therefore its division operator. That was
the reason I had asked you about the extended reals because you and I
know that their division operator cannot be compared with the field
divison operator.

[Fraley and Walther]
I have not actually set eyes on it, but the I'm pretty sure the
exact reference is:
R. Fraley and S. Walther, "Proposal to Eliminate Denormalised
Numbers", ACM SIGNUM Newsletter, pp. 22--23, No. si-2,  v. 14
(October, 1979).
It is available through the ACM portal.  Unfortunately you need to
be a subscriber.  I am not, which is why I have not read it.

Again thanks.

[Skipped]
I have skipped this section because it is getting very long and if
we propagate it the letter will be growing even longer. Thanks
for explaining so much, but I propose that you assume that I have
enough knowledge to discuss IEEE 754. If I don't understand something,
I will ask you. If you have a specific question concerning the skipped
section, ask them.

One of the cases where the sign of zero is important is when using
the copysign function.  copysign(x, -0) = -|x| and copysign(x, +0) =
|x|.  But this just illustrates my point.  No matter how someone
chooses to define this function, there will be a discontinuity at
zero, and know choice will be appropriate for every application.
The IEEE version has the advantage that it will often give the
desired result since zeros often arise due to underflow, and so when
this occurs, copysign delivers the result as if underflow had not
occurred.  N.B. underflow can only occur as a result of
multiplication or division.
  If one of them is "zero", what does the other one represent and why
  on earth are they compared as equal ?
The point is the distinction is invisible to the ordinary floating
point numbers.

What I wanted to point out is that FP numbers are
mixing two different concepts, numbers and limits.
There is only *one* number zero with signum zero, but *two*
**distinguishable** limits approaching zero from left and right
with signum 1 and -1. IEEE 754 looks like implementing the two limits,
but makes their comparison **indistinguishable** and assigns
them signum 0 or sometimes -1,1.

Together with the alliance to eradicate flags and traps
Traps and flags have been a feature of hardware since very early on,
but language designers and hardware designers hate them.  Language
designers hate them because they have global effects that completely
ignore any scoping rules, are difficult to fit into the underlying
models on which most languages are based and can unpredictably break
the flow of a program. Designers of high performance hardware hate
them because they can impose strict constraints on the order in
which otherwise unrelated events occur and can cause already started
operations to abort, requiring complicated unwinding of partially
completed operations.

Language yes, hardware no.
All C clones have the same problems: They either concentrate on
return values popped on the stack or use exception handling. Both
cases are bad for FP traps because they are *not* errors and should
not be handled like that. They are indicating a potentially
resolvable status.
Now the hardware thing: A trap is nothing else than an interrupt
caused by the FP unit, normally on IRQ channel 13 on the 80x86.
The interrupt handler is called (leaving the current state
unchanged), executing code and returning with IRET.

As *all* hardware is communicating with the processor in this
way I would be very astonished that there would be a remarkable
performance penalty.

it may cause strange and irregular
sign problems (if the +-infinity is eradicated in a denominator).
And that is not a freak-case; it was the very reason I stumbled
over the signed zero because my program (Java) did exactly this.
It sounds like you gripes might be more with Java, than with the
IEEE standard floating-point.

What language are you using ? And can you send me some source
code of you to see how you are handling IEEE 754 ?

If the sign of zero bothers you, ignore it.  It will only cause
you problems if an overflow occurs and does not resolve itself, in
which case you will end up with +/-infty or NaN, or if you use
copysign, in which case you should be paying more attention
anyway.
The point is, for debugging the programmer should probably enable
the overflow and divide by zero traps.  The default trap handlers
usually start a debugger (Microsoft Windows), or cause a core dump,
that can be read in by a debugger (Linux).  This way he/she can find
where the problems occur.  Where overflows or divide-by-zero are a
problem (usually as a result of underflow), the program can test the
results for infinity or NaN, and take appropriate action.  This is
far simpler than trying to determine if these conditions will occur
before the operation is performed.  Of course, the first step is to
try and anticipate the problems ahead of time.

That is really a nice theory that neg zero could be always
safely ignored.
Well, let's imagine the following situation: You have a GPS routine
giving you back the degrees, the minutes and the seconds in a double
array. Mind you, minutes and seconds are always positive.
HMS Titanic II approaches the equator and the autopilot
is on. What happens if the steering routine ignores the sign ?

Best,
Thorsten

754 | revision | FAQ | references | list archive