Real.maxFinite and friends

Stephen Weeks MLton@sourcelight.com
Fri, 5 Oct 2001 14:41:50 -0700


> I made Real.maxFinite and friends constants stored in
> src/runtime/basis/Real_const.S.  Now the
> src/basis-library/misc/primitive.sml has declarations of the form
> val maxFinite = _ffi "Real_maxFinite": real;

Is there any problem with making maxFinite and frieds _prim's?  This
would avoid keeping them in closure records, which is definitely what
will happen now, as the following code demonstrates.

fun f x =
   if x < 0.0
      then Real.maxFinite
   else f (f (x - 1.0))

val _ = f 13.0

> This means I could imagine the following program:
> 
> val r = Real.minNormalPos / 3.0
> val b1 = Real.isNormal r
> val b2 = Real.>(r, 0.0)
> val b3 = Real.<(r, Real.minNormalPos)
> val b = b1 andalso b2 andalso b3
> val _ = print ((Bool.toString b) ^ "\n")
> 
> would print true.  It currently prints true if you move Real.isNormal to
> be the last test.

> Whoops, this always prints false;  I thought isNormal would return true if
> the value were 0.0, but normals are non-zero.  But, I think you see what
> I'm getting at.
> 
> As it stands, the fact that b2 and b3 are both true is somewhat strange.

This doesn't seem strange to me.  r is just some positive *non-normal*
number.  Dividing minNormalPos by 3 creates a perfectly sensible 64
bit *non-normal* number.  I think you are confusing minNormalPos and
minPos.

> Here is an even stranger example:
> 
> val r = Real.minNormalPos / 3.0
> val b1 = Real.==(0.0, r)
> val _ = print ((Bool.toString b1) ^ "\n")
> val b2 = Real.==(0.0, r)
> val _ = print ((Bool.toString b2) ^ "\n")
> 
> I can justify outputs of (true, true), (false, true), (false, false).
> (true, true) is the correct result, according to the Basis Library, which
> MLton produces with -ieee-fp. 

I am confused.  When I try this program with -ieee-fp true, I get 

false
false

both with 20010706 and the forthcoming release.  I also get the same
result when compiling -ieee-fp false.  Hmmm, and looking at the code,
that seems reasonable.  Again, perhaps you mean Real.minPos instead of
Real.minNormalPos?  For me, that gives the same results as you said,
plus it agrees with the rest of what you wrote.

> (false, true) is what MLton (and SML/NJ)
> currently produces, which is explained by the fact that the value r is
> stored to memory during the non-tail and C calls that make up the print
> expression, and is fetched back for the second Real.==.  I would expect
> (false, false) to be reasonable output of MLton after commonSubexp has
> been moved to SSA, because the second Real.== will just inherit the value
> computed by the first.  (This isn't done by the current commonSubexp
> because the two Real.== are in different scopes.)

Makes sense.

> Anyways, this just tends to make my head hurt.  And since no numerical
> analysts have complained, I'm not that concerned.

I read through the rest of the email on Reals and my head now hurts as
well.  Here are my conclusions.

1. There is no easy way to get a sensible semantics keeping around
both 80 bit and 64 bit.

2. Using only 64 bit (with -ieee-fp true) is too costly.

3. In the long run we should move to 80 bit.

4. In the short run, don't worry about bouncing floats to memory for
*any* primitives.  I.E., treat isNormal just like we treat +.  I
realize that this will make the C and native backends different, but
I'm ok with that.  To make amends with numerical analysists, we keep
around -ieee-fp true and add a new primitive, MLton.Real.to64, which
forces 80 to 64.  That way, if someone needs to write a sensitive
algorithm, they can, without paying the entire cost of -ieee-fp true.

> BTW, Steve, could you see what the MLKit's native backend does with
> real7.sml, with and without the hack.

Unfortunately, they don't have any of Real.{isNormal, maxFinite,
minNormalPos, minPos}.