[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
Re: Zeroes and infinities
Thorsten Siebenborn wrote:
Peter Henderson wrote:
Thorsten Siebenborn wrote:
A rogue comment:
[...]
.
.
.
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.
.
.
.
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, and it allows overflow and
divide by zero to be indicated without having to check flags or causing
a core dump. 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.
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:
It is not just my opinion. A number of authors involved in the
standards process have stated this explicitly. The only reference to
this, that I have immediately to hand, is "What Every Computer Scientist
Should Know About Floating Point Arithmetic". There is a pointer to it
at http://grouper.ieee.org/groups/754. All the ideas I have presented
in my post come from such sources.
f(-0.0) = lim f(x) for x->-0
f(0.0) = lim f(x) for x->+0
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. You can deal with discontinuities at
zero by assigning different values to f(-0) and f(+0). This is useful
for functions such as the complex logarithm, where a discontinuity is
unavoidable and the convention is to place it along the negative real axis.
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.
.
.
.
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].
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.
.
.
.
When the original IEEE 754 standard was being written, there was a
proposal by Fraley and Walther, to have special overflow and
underflow symbols. It is described in "Analysis of Proposals for the
Floating-Point Standard", W. J. Cody, Computer, pp,. 63--68, v. 14,
no. 3 March, 1981. Presumably the actual proposal is given in ACM
SIGNUM Newsletter, special issue on the Proposed IEEE Floating-Point
Standard, October 1979.
Thank you. Another member already pointed out the idea of Fraley
and Walther, but didn't know when or where it was publicized.
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.
.
.
.
I doubt that the infinities or signed zeros are in any case better
suited or easier to understand than ov's and un's. The payoff between
rise of complexity and advantages may be a valid counterargument,
In many ways, the IEEE 754 system is the case of ov's and un's reduced
to their simplest form. I don't see the IEEE approach as particulary
complicated. The flaw is that there is a relucance to express the
principles behind it explicitly in the standard. The algebra of limits
theorem(s), on which these principles are based, is widely taught early
in college mathematics.
I translate:
You think that I think that FP numbers are intervals.
I actually wasn't thinking that as such. However, in this context, this
idea is closely related to the idea of invoking infinitessimals.
But the "only
interpretation that works" is that every FP number represents itself
This is necessary to allow you to define what the results of the
floating point operations should be. For example, the result of
floating point addition is the result of real addition of the floating
point operands after rounding. The big feature of the original IEEE
standard was that it insisted that this be done properly.
when you already mentioned that infinity is a result of a
limiting operation.
The difficulty here is we are starting to head into real philosophy,
i.e. what are numbers? Infinity is a value appended to the real
numbers. We use a limiting process to determine what value should
result from using infinity as an operand.
A few lines later you suddenly tell us that for +0/-0 a "partial
exception" must be made because in that case the "only working
interpretation" ceases to function.
No, it has to be extended, as part of the procedure of extending the
reals to include infinity.
You are also
claiming that both values represent zero and that they
should compare equal.
That is what the standard intends.
But you are *also* claiming that they are in fact limits
according to the direction the variable uses because it is the
only reason how the use of 1/+0 and 1/-0 with different results
can be explained.
I am not saying they are limits. The IEEE standard defines addition,
subtraction, multiplication and division tables for the set of floating
point numbers, together with infinity and with signed zero. But such
tables would be of little interest if there was not a strong
mathematical motivation for them. For the ordinary floating point
numbers, it is the standard definition of these operations. For the
special values, it is provided by the algebra of limit theorem.
In fact you have summarized all the problems
I encounter with signed zeroes
.
.
.
- There is only one value *zero* and besides that plus and minus zero
*cannot* both represent the same number because they violate the
fundamental identity that for x == y => f(x) == f(y)).
In the case x and y are not zero, this identity holds, as no attempt is
made to indicate the direction from which x is approached. For x and y
zero, perhaps with different signs, this identity still holds, provided
f is continuous at x. If a function appearing in a program has a
discontinuity, then any calculations near the discontinuity need to be
treated with considerable care. The difficulties arise not because zero
has a sign, but because the function is discontinuous. The programmer
has to think about what to do in this situation. Often, there is a
preferred definition of the function at the point of discontinuity, but
this is merely a convention, and only some of the time is it the right
choice for a particular application. For example, with the complex
logarithm, the principle value of ln(-1) is conventionally pi. But if
you have an application where im(z) <= 0 when re(z) < 0, then the proper
value to use for ln(-1) is nearly always -pi. On the other hand, with
this example, if the next operation is exp(z), the value chosen doesn't
matter, as exp(z) is blind to the difference.
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.
.
.
.
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.
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.
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.
Of course, I am assuming you are not a compiler writer, a
floating-point hardware designer, a library routine author or a
standards committee member.
Of course I assume you are a standards committee member?
No I am not. You have misunderstood my intent. I meant ordinary
programmers can ignore these issues. Of course they can't ignore
overflows or divide-by-zero, but then, this has always been the case,
even when zero did not have a sign and infinity was not available.
Compiler writers and hardware designers have a responsibility to
implement the standard correctly. Of course, standards committee
members also have to understand these things. As for library routine
authors, using these facilities allows them to write faster code that
produces more accurate results and handles everything thrown at it.
Regards,
Peter Henderson