[MLton-devel] Real.fromLargeInt

Stephen Weeks MLton@mlton.org
Wed, 23 Oct 2002 13:05:43 -0700


> Real.fromLargeInt's pos function doesn't seem to be affected by the
> current rounding mode.  Furthermore, ~ (pos (IntInf.~ i)) won't always
> give the correct result for some rounding modes. 

Agreed.  I've attached my latest attempt, which fixes these problems,
below.

> Real.toLargeInt doesn't seem to work for "large" real numbers.

Oops, that's due to a separate problem with Real.split that is fixed
in my branch, but not in the trunk.  Real_modf is incorrectly marked
as Functional, when it should be marked SideEffect.  Here's the fix.

diff -c -r1.36 -r1.36.2.1
*** prim.fun	25 Aug 2002 22:23:57 -0000	1.36
--- prim.fun	23 Oct 2002 00:38:26 -0000	1.36.2.1
***************
*** 351,357 ****
  	  (Real_ldexp, Functional, "Real_ldexp"),
  	  (Real_le, Functional, "Real_le"),
  	  (Real_lt, Functional, "Real_lt"),
! 	  (Real_modf, Functional, "Real_modf"),
  	  (Real_mul, Functional, "Real_mul"),
  	  (Real_muladd, Functional, "Real_muladd"),
  	  (Real_mulsub, Functional, "Real_mulsub"),
--- 351,357 ----
  	  (Real_ldexp, Functional, "Real_ldexp"),
  	  (Real_le, Functional, "Real_le"),
  	  (Real_lt, Functional, "Real_lt"),
! 	  (Real_modf, SideEffect, "Real_modf"),
  	  (Real_mul, Functional, "Real_mul"),
  	  (Real_muladd, Functional, "Real_muladd"),
  	  (Real_mulsub, Functional, "Real_mulsub"),

> Some might argue that it is the responsibility of the maintainer of the
> basis-2002 branch to bring the behaviour into accordance with the new
> spec.  Others might counter with the argument that because the function
> wasn't present when basis-2002 was branched, it is the responsibility of
> the author.  ;)

Others might argue that branching times are irrelevant and that it is
the responsibility of the developer who merges into the trunk to make
sure that the trunk matches the spec.  So, since my changes are being
made on the on-20021018-prof-branch, then the responsibility falls to
whoever merges last.  :-)

Nevertheless, in anticipation of you merging first, I have made
fromLargeInt take into account the rounding mode.

--------------------------------------------------------------------------------

      local
	 fun negateMode m =
	    case m of
	       TO_NEAREST => TO_NEAREST
	     | TO_NEGINF => TO_POSINF
	     | TO_POSINF => TO_NEGINF
	     | TO_ZERO => TO_ZERO

	 val m: int = 52 (* The number of mantissa bits in 64 bit IEEE 854. *)
	 val half = Int.quot (m, 2)
	 val two = IntInf.fromInt 2
	 val twoPowHalf = IntInf.pow (two, half)
      in
	 fun fromLargeInt (i: IntInf.int): real =
	    let
	       fun pos (i: IntInf.int, mode): real = 
		  case SOME (IntInf.log2 i) handle Overflow => NONE of
		     NONE => posInf
		   | SOME exp =>
			if Int.< (exp, Int.- (valOf Int.precision, 1))
			   then fromInt (IntInf.toInt i)
			else if Int.>= (exp, 1024)
		           then posInf
			else
			   let
			      val shift = Int.- (exp, m)
			      val (man: IntInf.int, extra: IntInf.int) =
				 if Int.>= (shift, 0)
				    then
				       let
					  val (q, r) =
					     IntInf.quotRem
					     (i, IntInf.pow (two, shift))
					  val extra =
					     case mode of
						TO_NEAREST =>
						   if IntInf.log2 r =
						      Int.- (shift, 1)
						      then 1
						   else 0
					      | TO_NEGINF => 0
					      | TO_POSINF =>
						   if IntInf.>= (r, 0)
						      then 1
						   else 0
					      | TO_ZERO => 0
				       in
					  (q, extra)
				       end
				 else
				    (IntInf.* (i, IntInf.pow (two, Int.~ shift)),
				     0)
			      (* 2^m <= man < 2^(m+1) *)
			      val (q, r) = IntInf.quotRem (man, twoPowHalf)
			      fun conv (man, exp) =
				 fromManExp {man = fromInt (IntInf.toInt man),
					     exp = exp}
			   in
			      conv (q, Int.+ (half, shift))
			      + conv (IntInf.+ (r, extra), shift)
			   end
	       val mode = getRoundingMode ()
	    in
	       case IntInf.compare (i, IntInf.fromInt 0) of
		  General.LESS => ~ (pos (IntInf.~ i, negateMode mode))
		| General.EQUAL => 0.0
		| General.GREATER => pos (i, mode)
	    end

	 val toLargeInt: IEEEReal.rounding_mode -> real -> IntInf.int =
	    fn mode => fn x =>
 	    (IntInf.fromInt (toInt mode x)
 	     handle Overflow =>
	     case class x of
		INF => raise Overflow
	      | _ => 
		   let
		      fun pos (x, mode) =
			 let 
			    val {frac, whole} = split x
			    val extra =
			       if mode = TO_NEAREST
				  andalso Real.== (frac, 0.5)
				  then
				     if Real.== (0.5, realMod (whole / 2.0))
					then 1
				     else 0
			       else IntInf.fromInt (toInt mode frac)
			    val {man, exp} = toManExp whole
			    (* 1 <= man < 2 *)
			    val man = fromManExp {man = man, exp = half}
			    (* 2^half <= man < 2^(half+1) *)
			    val {frac = lower, whole = upper} = split man
			    val upper = IntInf.* (IntInf.fromInt (floor upper),
						  twoPowHalf)
			    (* 2^m <= upper < 2^(m+1) *)
			    val {whole = lower, ...} =
			       split (fromManExp {man = lower, exp = half})
			    (* 0 <= lower < 2^half *)
			    val lower = IntInf.fromInt (floor lower)
			    val int = IntInf.+ (upper, lower)
			    (* 2^m <= int < 2^(m+1) *)
			    val shift = Int.- (exp, m)
			    val int =
			       if Int.>= (shift, 0)
				  then IntInf.* (int, IntInf.pow (2, shift))
			       else IntInf.quot (int,
						 IntInf.pow (2, Int.~ shift))
			 in
			    IntInf.+ (int, extra)
			 end
		   in
		      if x > 0.0
			 then pos (x, mode)
		      else IntInf.~ (pos (~ x, negateMode mode))
		   end)
      end


-------------------------------------------------------
This sf.net email is sponsored by: Influence the future 
of Java(TM) technology. Join the Java Community 
Process(SM) (JCP(SM)) program now. 
http://ads.sourceforge.net/cgi-bin/redirect.pl?sunm0002en

_______________________________________________
MLton-devel mailing list
MLton-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mlton-devel