Path: utzoo!mnetor!uunet!husc6!bloom-beacon!gatech!mcnc!decvax!decwrl!sun!quintus!ok From: ok@quintus.UUCP (Richard A. O'Keefe) Newsgroups: comp.lang.prolog Subject: Re: Arithmetic problems with Quintus Prolog Message-ID: <873@cresswell.quintus.UUCP> Date: 13 Apr 88 07:50:32 GMT References: <11111@shemp.CS.UCLA.EDU> <869@cresswell.quintus.UUCP> Organization: Quintus Computer Systems, Mountain View, CA Lines: 202 In article , eggert@sea.sm.unisys.com (Paul Eggert) writes: > In article <869@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) > writes: > >> 0.0 -vs- -0.0 -- mandated by IEEE ... >> 1 \== 1.0 -- implicitly mandated by IEEE > > The IEEE standard does not implicitly mandate that 1 \== 1.0. Yes, the IEEE standard _does_ implicitly mandate that 1 \== 1.0. Why? let X = 1 - 1. By the rules of *integer* arithmetic, X is completely indistinguishable from -X. let Y = 1.0 - 1.0. By the rules of *IEEE floating-point* arithmetic, Y is clearly and easily distinguishable from -Y. For example, if you have access to an IEEE-conforming machine with a C compiler, try the following program: #include void foo(a, b) double a, b; { double y = a-b; printf("%f %f\n", copysign(1.0, y), copysign(1.0, -y)); } main() { foo(1.0, 1.0); exit(0); } I got the answers 1.000000 and -1.000000. Even more simply, after doing x = 1.0 - 1.0; it is the case that 1.0/x > 0.0 but 1.0/(-x) < 0.0 [Quintus Prolog currently reports division by 0.0 and division by -0.0 as errors, so you can't test this. I tested it in C. Whether division by zero _should_ be reported as an error on an IEEE machine is debatable, but the standard permits it. ] Let me summarise my argument, which by the way is _not_ a defence of the way Quintus Prolog currently handles floating point, but a defence of drawing a distinction between integer and float in _any_ Prolog. (1) The IEEE 754 standard draws a clear distinction between 0.0 and -0.0. This is not an accidental feature falling out of the binary representation, it is a deliberate aspect of the standard which has been carefully thought out and described in some detail. Therefore, anything which ignores the distinction between 0.0 and -0.0 violates the IEEE 754 standard, even if it otherwise uses the IEEE formats. (2) I proposed as a design principle for Prolog that "things which behave differently should not be reported as identical by == and \==". More logically, numbers which do not have identical numerical properties should not unify. =:= and =\=, which do numeric comparisons, should of course do whatever the IEEE standard says numeric comparisons should do. (3) While Prolog should be useful on IBM mainframes, VAXen, Pyramids, and all sorts of things which either don't use IEEE formats or in some other respect fail to satisfy the IEEE 754 requirements, it would not be a good idea to define Prolog so that it _must_ violate the IEEE standard. (4) It follows from (1), (2), and (3) that the IEEE standard "mandates" 0 \== 0.0. (It also mandates 0 =:= 0.0, but that is another story.) (1) is a brute fact. (3) is an opinion, but in 1984 Paul Eggert was so far in agreement with (3) as to suggest that there should be a ``standard'' way of representing IEEE NaNs in Prolog. Perhaps the weakest point in this "proof" is (2), but that's the weakest definition of == I can come up with which leaves it doing anything useful. (5) For any integer N for which all expressions are defined, if ZI is N-N, NF is float(N), ZF is NF-NF then ZF is distinguishable from -ZF by IEEE rules, but ZI is not distinguishable from -ZI. Therefore the expressions N-N and NF-NF behave differently (they have distinguishable results) and therefore N and NF are distinguishable. is_integer(X) :- number(X), PZ is X-X, MZ is -PZ, PZ == MZ. %or: copysign(1.0,float(PZ)) =:= copysign(1.0,float(MZ)). is_float(X) :- number(X), PZ is X-X, ( PZ =\= PZ -> true % PZ is a NaN ; MZ is -PZ, PZ \== MZ %or: copysign(1.0,float(PZ)) =\= copysign(1.0,float(MZ)) ). The check for PZ =\= PZ is to cope with IEEE Infinities, which yield quiet NaNs when subtracted from themselves. A NaN is supposed to be arithmetically unequal to anything, including itself. Please bear in mind that the way is_integer/1 works is dictated by the rules of mathematical integer arithmetic (and machine integer arithmetic too on most machines), and that the way is_float/1 works is dictated by the rules of IEEE 754 floating-point arithmetic. Again, this follows from (1) -- because the integer/float test boils down to seeing whether zero is signed or unsigned --, (2), and (3). This is why I claim that the IEEE standard *implicitly* mandates that 1 is not identical to 1.0. In fact, if you include the IEEE 754 function copysign() in your Prolog's repertoire of arithmetic forms, the proof doesn't even rely on (2), just on the IEEE rules. Were you surprised to see that the standard for floating point arithmetic contains things which are not equal to themselves? The NaN produced by Infinity-Infinity is identical to itself, but not equal to itself. What price logic, eh? You can escape from the conclusion that integers should be distinct from floats only by -- saying that a Prolog definition of floating-point arithmetic which is _necessarily_ incompatible with IEEE arithmetic is acceptable to you, or -- by rejecting my suggestion (2), that == should not report as identical things which behave differently AND by saying that you never want an IEEE-conforming copysign(). Paul Eggert further writes > Consider the following rule, used by Silogic Prolog: > > If a number can be exactly represented in both floating point and > integer formats, then use integer format. > With this rule 1 == 1.0, and 0 == 0.0, but 0 \== -0.0 because 0 and -0.0 are > not the same number. That's cunning. That's really cunning. But 1.0-1.0 is supposed to give you 0.0, and with this scheme you will get 0, which is the wrong answer. > This rule matches most people's intuition better than Quintus's conversion > rules. It's not as fast as Quintus's arithmetic, but if you prize correct > answers over fast ones, you will insist on overflow checking anyway, and you > may find that a cheap test for the question "Is the number X exactly > representable in integer format?" folds naturally into overflow checking. I absolutely agree that correct answers are to be preferred to fast ones. And I repeat that the point of this message is not to defend Quintus Prolog's arithmetic, only the rigid distinction between integer and float. It doesn't matter how cheap the test for fitting in an integer format is, if you convert any floating-point numbers to integers, you'll end up violating the IEEE standard even on a machine whose FPU supports is. The way you do overflow checking on most machines is to wait for the floating-point exception; I don't see any way of folding a test for fitting into an integer into that. Note that just because not(1 = 1.0) and not(1 == 1.0), it does not follow that 1 =\= 1.0. One could even define arithmetic comparison to use a "fuzz" like APL's []CT. And we just saw that in IEEE arithmetic it does not follow from X=X and X==X that X=:=X! As for "most people's intuition", (a) claims like this are worthless unless you have actually done at least a sample survey [I have no more idea of what "most people" do or don't find intuitive than Paul Eggert], (b) imagine me sobbing with pity for all those people using C and Pascal and ADA and Lisp whose intuition is being so rudely violated by the distinction between integers and floats those languages force on them, oh pitiful! pitiful!, and (c) Suppose you have a Prolog system, a Common Lisp system, and a C system, all using the same precision of IEEE floating-point numbers. Then C and Lisp would print the same answers for some expressions for which a Prolog using the C-Prolog or Silogic rules would print different answers. This is "intuitive"? Let's face it, playmates, floating-point arithmetic is NOT simple, it is NOT intuitive, and throwing in gratuitous conversions in the interests of "intuition" is going to make the actual behaviour of the system even LESS intuitive. Floating-point arithmetic is subtle enough that I will thank vendors (including Quintus) to keep their paws _off_ the floating- point representation. Just as a matter of interest: how many people reading this newsgroup really care about doing floating-point arithmetic in Prolog (as opposed to receiving floating-point numbers from another language and giving them back, with most FP computations being done in the other language)? Are there many people out there who are really interested in things like IEEE- conformance?