Date: Fri, 04 Jul 1997 17:14:17 -0800
From: "Jerome T. Coonen" 
To: numeric-interest@validgh.com
Subject: A proposal for RealJava

A Proposal for RealJava
========================================================
========================================================

"Write once, run with speed and accuracy -- everywhere."

by Jerome Coonen
Draft 1.0, July 4, 1997
(email version best viewed with fixed-pitch font)

Table of Contents
=================

Introduction
RealJava Is
The Bottom Line
Floating Point Architectures
A Model for Portable Programming
  The type hierarchy
  A fast, accurate loop
  Floating literals
  A method call
  Handling floating point state
RealJava Features
  Types
  Expression evaluation
  Conversions and promotions
  Contraction operators
  Literals
  Floating point environment
  Rounding modes
  Exception flags
The RealJava Virtual Machine
  Virtual Machine operations
  RealJVM idioms
Expression Evaluation Examples
  Method invocation
Changes to the package java.lang
RealJava and IEEE 754
Java Expression Evaluation and Optimization
RealJava FAQ


Introduction
============

Designers of the "100% Pure" Java (tm) language have attempted
to guarantee numerical robustness by constraining the language
so that all numerical computations would produce identical
results across all Java platforms.  This constraint, however,
comes at a high price on platforms supporting wide intermediate
registers or extra-precise operations such as "fused multiply-
add." On these platforms, 95% or more of the total, users could
expect to get better answers faster than permitted by Pure Java's
four-function float/double model.

RealJava extends Pure Java to support an alternative approach --
a programming model giving portable access to the features
of all Java platforms.  RealJava derives from the belief that
users want accurate answers delivered at the highest speed,
rather than less accurate results delivered more slowly, whether
or not they match results on less capable machines.

RealJava also brings Java into compliance with IEEE standard
754 for binary floating point arithmetic.

This document presents the essential features of RealJava first,
and then continues with further details and examples of the
programming model.  You are invited to address comments to the
author at jerome@be.com.


RealJava Is
===========

0) dedicated to the proposition that loops of the form

      double s, x[], y[];
      for (int i=0; i=, or >, and that it not be raised when
unordered operands are compared with the relations == and !=.
For this reason, there need be only one comparison bytecode that
doesn't signal invalid for unordered operands.  By (arbitrary)
convention, unordered operands appear greater than with this
operator, but this doesn't affect code generation.


Expression Evaluation Examples
==============================

Expression evaluation is completely determined by the bytecodes emitted
by the RealJava compiler. It's at the source level that programmer's
determine expression evaluation will be used.  Here is a list of typical
numerical expressions, with byte counts greater than 1 given.

1) double a, b, x, y;
   y = a*x + b;
     
  Natural:
     dloadn b   // (2 bytes) simple fp register load
     dloadn a   // (2)
     dloadn x   // (2)
     (contract) // (permit fused-mul add, if enabled by programmer)
     dnmul      // (2) native multiply on any platform
     dnadd      // (2) native add
     dnstored y // (2) store, after implied narrowing of doubleN

  Strict:
     dload b
     dload a
     dload x
     (contract) // (permit fused-mul add, if enabled by programmer)
     dmul       // coerce result to double if product is wide
     dadd       // coerce result to double...
     dstore y

2) doubleN s;
   double a, b;
   s = s + a*b;
   
  Natural:
     dnload s   // (2) load doubleN value
     dloadn a   // (2) fp register load of double value
     dloadn b   // (2)
     (contract)
     dnmul      // (2)
     dnadd      // (2)
     dnstored s // (2) convert to double and store

  Strict:
     Same as example (1), with s, a, and b all of type double.

3) double z;
   float e, f;
   z = z + e*f;
  
  Natural:
     dloadn z   // (2) load double to fp register
     floadn e   // (2) load float to fp register
     floadn f   // (2)
     (contract) // (if the fn2dn is a nop, the mul-add can fuse)
     fnmul      // (2)
     fn2dn      // (3)
     dnadd      // (2)
     dnstored z // (2) store, with implied narrowing

  Strict:
     dload z
     fload e
     fload f
     fmul
     f2d         // this conversion inhibits fusing the mul and add
     dadd
     dstore z

Method invocation
-----------------

Java supports overloaded methods, with resolution of types at
compile time, and type conversion if necessary.  Java specifies
the choice of overloaded methods according to the types of the
operands.  Pure Java specifies widening, when necessary to match a
method's signature.  RealJava adds implicit narrowing of doubleN
to double and floatN to float, when necessary.

Here is some typical examples of method invocation.  In the first
case, both doubleN and double forms of the method are defined.

doubleN compound(doubleN r, doubleN n); // return (1+r)^n
double  compound(double  r, double  n);
int n;
double p, s, t;
p = compound(s * t, n);
  
  Natural:
     dloadn s   // (2)
     dloadn t   // (2)
     dnmul      // (2)
     iload n
     i2dn       // (2)
     
     dnstored p // (2)

  Strict:
     dload s
     dload t
     dmul
     iload n
     i2d
     
     dstore p

The compiler narrows doubleN expressions to double, if
necessary.

double compound(double r, double n); // return (1+r)^n
int n;
double p, s, t;
p = compound(s * t, n);
  
  Natural:
     dloadn s   // (2)
     dloadn t   // (2)
     dnmul      // (2)
     dn2d       // (2) implicit narrowing during natural evaluation
     iload n
     i2d
     
     dstore p

  Strict:
     Same as previous example
     
And here's what happens if the only definition is doubleN:

doubleN compound(doubleN r, doubleN n); // return (1+r)^n
int n;
double p, s, t;
p = compound(s * t, n);
  
  Natural:
     Same as the first compound() example, with double and 
     doubleN forms.

  Strict:
     dload s
     dload t
     dmul
     d2dn       // (2)
     iload n
     i2dn       // (2)
     
     dnstored p // (2)

When a fast float version of a library method is desired, just
define it with float or floatN arguments.  When evaluation is
strict, float expressions are promoted to double before floatN
in order to match a method's signature.

doubleN annuity(doubleN r, doubleN n); // return (1 - (1+r)^-n)/r
double  annuity(double  r, double  n);
floatN  annuity(floatN  r, floatN  n);
int n;
float a, b, c;
a = annuity(b / c, n);

  Natural:
    floadn b   // (2)
    floadn c   // (2)
    fndiv      // (2)
    iload n
    i2fn       // (2)
    
    fnstoref a // (2)
    
  Strict:
    fload b
    fload c
    fdiv
    f2d        // preferred over f2fN during strict evaluation
    iload n
    i2d
    
    d2f
    fstore a


Changes to the package java.lang
================================

Here is a summary of changes to java.lang that reflect the new floating
types:

Add classes for types floatN, doubleN, and longDouble.
Add methods to all floating classes to support conversions to
  and from new floating types.
Add methods to both integral classes to support conversions to
  and from new floating types.
Add signBit, nextAfter and copySign methods to all floating classes.
Add support for the floating point state to java.lang.Math.

java.lang.Float

  Add methods to support conversions to and from new floating types,
  rounded (rather than merely truncated) conversions to integral types,
  and methods from the IEEE 754 appendix.  Organize the class so that
  references to the natural and long double types occur after the
  float/double definitions.
  
  // class methods
  public static int signBit(float value);
  public static float copySign(float magVal, float signVal);
  public static float nextAfter(float srcVal, float targVal);
  // instance methods
  public int roundedIntValue();
  public long roundedLongValue();
  public int signBit();
  public float copySign(float signVal);
  public float nextAfter(float targVal);
  
  // advanced methods
  // constructors
  public Float(floatN value);
  public Float(doubleN value);
  public Float(longDouble value);
  // instance methods
  public floatN floatNValue();
  public doubleN doubleNValue();
  public longDouble longDoubleValue();

java.lang.Double

  Add methods corresponding to those added to java.lang.Float.

java.lang.DoubleN

  This class is new in RealJava.

public final class DoubleN extends number {
  // constants
  public static final doubleN MIN_VALUE = ;
  public static final doubleN MAX_VALUE = ;
  public static final doubleN NEGATIVE_INFINITY = -1.0dN / 0.0dN;
  public static final doubleN POSITIVE_INFINITY = 1.0dN / 0.0dN;
  public static final doubleN NaN = 0.0dN / 0.0dN;
  // constructors
  public DoubleN(doubleN value); // and promoted float, floatN, double
  public DoubleN(longDouble value);
  public DoubleN(String s) throws NumberFormatException;
  // class methods
  public static String toString(doubleN dn);
  public static DoubleN valueOf(String s)
    throws NullPointerException, NumberFormatException;
  public static int[] doubleNToIntBits(doubleN value);
  public static doubleN intBitsToDoubleN(int[] bits);
  public static boolean isNaN(doubleN dn);
  public static boolean isInfinite(doubleN dn);
  public static int signBit(doubleN value);
  public static doubleN copySign(doubleN magVal, doubleN signVal);
  public static doubleN nextAfter(doubleN srcVal, doubleN targVal);
  // instance methods
  public String toString();
  public boolean equals(Object obj);
  public int hashCode();
  public int intValue();
  public int roundedIntValue();
  public long longValue();
  public long roundedLongValue();
  public float floatValue();
  public double doubleValue();
  public floatN floatNValue();
  public longDouble longDoubleValue();
  public boolean isNaN();
  public boolean isInfinite();
  public int signBit();
  public doubleN copySign(doubleN signVal);
  public doubleN nextAfter(doubleN targVal);
}

java.lang.FloatN

  This new class corresponds to java.lang.DoubleN.

java.lang.longDouble

  This new class corresponds to java.lang.DoubleN.

java.lang.Math

  In RealJava, the methods in Math are defined with doubleN and
  floatN arguments, rather than the double and float of Pure Java.
  This provides fast, accurate library functions on all platforms,
  whether evaluation is strict or natural.

  RealJava adds a number of constants and methods to handle floating
  point state.
  
  public static final int DEFAULTENV = 0x00000000;
  public static final int TONEAREST  = 0x00000000;
  public static final int UPWARD     = 0x08000000;
  public static final int DOWNWARD   = 0x04000000;
  public static final int TOWARDZERO = 0x0C000000;
  public static final int ROUNDMASK  = 0x0C000000;
  public static final int INEXACT    = 0x00000020;
  public static final int DIVBYZERO  = 0x00000004;
  public static final int UNDERFLOW  = 0x00000010;
  public static final int OVERFLOW   = 0x00000008;
  public static final int INVALID    = 0x00000001;
  public static final int FLAGMASK   = 0x0000003D;
  public static int getEnv();
  public static void setEnv(int env);
  public static int holdEnv();
  public static void updateEnv(int env);
  public static int getRound();
  public static void setRound(int round);
  public static void clearExcept(int excepts);
  public static int testExcept(int excepts);
  public static void raiseExcept(int excepts);


RealJava and IEEE 754
=====================

RealJava claims full conformance to the IEEE standard for binary
floating point arithmetic, in the sense that it complies with every
"shall" directive in the standard.  For IEEE 754 aficionados, here
is a list of subtle conformance issues:

* The standard specifies optional traps associated with the
  exceptions overflow, underflow, invalid, divide by zero,
  and inexact.  RealJava does not support traps, primarily
  because the cost of doing so portably would outweigh the
  apparent benefit.

* The standard requires that binary-decimal conversion be performed
  with worst-case extra error 0.47 unit in the last place.  RealJava
  like Pure Java, requires correct rounding for all binary-decimal
  conversions.  This removes all ambiguities in conversions at
  negligible added cost over the "traditional" algorithms with
  their slightly looser error bound.

* The standard requires that comparisons involving the predicates
  <, <=, >=, and > raise the invalid exception when one or both
  of the operands is NaN.  RealJava supports this requirement,
  through extensions to Pure Java's comparison operators.

* The standard requires that results be rounded under program
  control.  RealJava supports the four types of rounding, using
  a dynamic mode setting.

* The standard requires, on machines whose natural evaluation type
  is double or extended, that it be possible to achieve results
  rounded according to what RealJava calls strict evaluation.
  RealJava requires strict evaluation as the default, though it
  admits natural evaluation for applications requiring higher
  performance (with generally increased accuracy).
  
* One of the best-known features of the standard is gradual
  underflow, whereby tiny results are "subnormalized" with the
  format's minimum exponent, rather than being simply "flushed"
  to zero.  RealJava and Pure Java require that the floating
  point engine implement gradual underflow.

* The standard specifies a class of signaling NaNs that trigger an
  invalid exception when they arise in any arithmetic operation.
  RealJava, by specifying a portable mechanism for the invalid
  exception flag, supports the invalid exception raised by the
  underlying floating point engine.
  
  The standard doesn't specify how to distinguish quiet NaNs from
  signaling NaNs, so the distinction varies from platform to
  platform.  Programmers should note that quiet NaNs may become
  signaling NaNs if passed between different platforms.  When a
  signaling NaN is used in an arithmetic operation, the invalid
  flag will be raised and the floating point result, if any,
  will be one of the platform's quiet NaNs.


Java Expression Evaluation and Optimization
===========================================

The Pure Java specification places strict demands on expression
evaluation, in the interest of attempting to achieve identical
results for all computations across all platforms.  A pleasant
side-effect is that the quality of numerical computation improves
with tighter specifications.  A well-known example, now past, is
the historic practice in C compilers of ignoring parentheses in
expressions such as (x + y) + z.  While addition is an associative
operation in mathematics, and even for 2's-complement integers,
floating point addition is not generally associative.

This section, derived from C9x and related work, mentions a list
of optimization pitfalls related to floating point computation.
All variables of any floating type.

* Constant expressions (i.e. those involving literal values) are
  evaluated at runtime.  Side effects from expressions like
  0.0 / 0.0 arise at runtime, even though the conversions may be
  performed at compile time.

* Don't replace (x == x) with true. The relation is not true of NaNs.

* Don't replace (x != x) with false. It is true of NaNs.

* Don't replace (x - x) with 0.0. The result is x if it's a NaN and
  invalid (a NaN) if x is infinite; otherwise, in RealJava, which
  supports IEEE rounding modes, the sign of the zero result depends
  on the rounding direction.

* Don't replace (0.0 * x) with 0.0. The sign of the result depends
  on x when x is finite, and the value depends on x when x is NaN
  or infinite.

* Don't replace (x + y) - y with x.  Addition is not associative.

* Don't replace x + x*y with (x + 1.0)*y.  The distributive law
  may fail in floating point computations, too.

* Don't simplify expressions like x + 0.0 and x - 0.0, and don't
  change -x to 0.0 - x.  In these cases, when x itself is zero the
  result sign will depend on the rounding direction.  Similarly,
  don't change x - y to -(y - x).  If that result is zero, the
  two expressions will have opposite sign. The expression
  x - y is the same as x + (-y).

* Don't change if (x < y) {block A} else {block B} to
  if (x >= y) {block B} else {block A}.  If x and y are unordered
  because one or both is NaN, block B should be executed and
  the invalid flag should be set.

* In expressions involving constants, the freedom to optimize may
  depend on whether the constant is exactly representable as a
  floating point value.  The expressions x / 5.0 and x * 0.2 are
  different because 0.2 must be rounded to a binary floating point
  value.  The expressions x / 2.0 and x * 0.5 are identical, as are
  1.0 * x, x, and x / 1.0.


RealJava FAQ
============

1.  Why the hack using magic boolean variables to enable features?

    An essential part of the purity of Java is its lack of a
    preprocessor.  Programmers already use private static booleans
    for condition compilation.  RealJava goes a step further.

2.  What if I just use float and double variables?

    You get 100% Pure Java expression evaluation.  RealJava permits
    different implementations of functions like sin() and exp() on
    different platforms, so you won't be guaranteed bit-identical
    results across all platforms, but you'll get pure float and double
    expression evaluation.
    
3.  Isn't it ugly to have the java.lang numerical classes grow as
    the square of the number of types?
    
    Yes, the Java language specification went the usual route of
    linearizing what is essentially 2D information about mixing and
    converting types.  There are compact, alternative representations.
    As it is, the classes can be organized to partition off the floatN,
    doubleN and longDouble methods.

4.  Does natural evaluation always yield better results on iA?

    No.  Although the results are "almost always" more accurate
    than their pure float/double counterparts, it's easy to
    construct examples where the extra information becomes
    misinformation which can in turn be promoted to have high
    relative error.
    
    Then there are the calculations designed for a specific
    precision.  These techniques can be used to simulate higher
    precision using just float or double values.
    
5.  What is the performance degradation on iA for a double inner
    product loop running with strict versus natural evaluation?
    
    Some tests have shown a factor of 10 or more slow-down due
    to the stores and loads required to coerce intermediate
    results to double on iA.
    
6.  What are the performance implications of the IEEE exception flags?

    Pure Java already constrains the order of execution to such
    an extent that inserting a flag test into a sequence should
    be possible.  The performance will depend on a platform's
    ability to interrogate the flags in hardware, which can often
    take far more time than a simple arithmetic operations because
    of the interface to the underlying system.

7.  Won't converting decimal constants at runtime degrade
    performance?

    No.  Each constant that appears must be converted just once,
    even if it appears in a loop.
    
8.  Doesn't the use of extended intermediate values precipitate
    the same portability problems as have 16, 32, and 64-bit widths
    for pointers and signed and unsigned integers?
    
    No.  The notion of "at least as much range and precision" is
    well defined and generally a good thing in numerical computing;
    because natural evaluation must be requested, it can be
    avoided when necessary.  Differences in the high-order bits
    of integral values, especially pointers, expose fundamental
    incompatibilities.


From owner-numeric-interest  Sat Jul  5 12:48:15 1997
Return-Path: 
Received: by validgh.com (SMI-8.6/SMI-SVR4)
	id MAA11855; Sat, 5 Jul 1997 12:47:07 -0700
Received: from europe.std.com by validgh.com (SMI-8.6/SMI-SVR4)
	id MAA11850; Sat, 5 Jul 1997 12:47:02 -0700
Received: from world.std.com by europe.std.com (8.7.6/BZS-8-1.0)
	id PAA15932; Sat, 5 Jul 1997 15:48:02 -0400 (EDT)
Received: from nixon (world.std.com) by world.std.com (5.65c/Spike-2.0)
	id AA29425; Sat, 5 Jul 1997 15:47:47 -0400
Message-Id: <3.0.1.32.19970705154810.00b6b8c0@world.std.com>
X-Sender: chase@world.std.com
X-Mailer: Windows Eudora Light Version 3.0.1 (32)
Date: Sat, 05 Jul 1997 15:48:10 -0400
To: numeric-interest@validgh.com
From: David Chase 
Subject: Re: A proposal for RealJava
Cc: "Jerome T. Coonen" 
In-Reply-To: <33BD9FE8.5564@be.com>
Mime-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Sender: owner-numeric-interest
Precedence: bulk
Reply-To: David Chase 
content-length: 5843

At 05:14 PM 7/4/97 -0800, Jerome T. Coonen wrote:
>A Proposal for RealJava
...
>Rounding modes
>--------------
>
>RealJava extends java.lang.Math to include methods to test and alter
>the rounding mode, as specified by the IEEE standard.  The two
>methods get and set the rounding mode as an integer value, whose
>possibilities are given by the constants.  The ROUNDMASK value permits
>a program to extract the rounding mode from an environment value or to
>insert one back in.
>
>The rounding modes apply to the floating point operations +, -, *, /,
>to specified conversion methods like roundedIntValue() and
>roundedLongValue(), and to methods like rint(), which rounds to an
>integral value within a floating format.  Other mathematical methods
>will differ with respect to the extent to which they honor the IEEE
>rounding modes.  Some methods deliver results rounded according to
>the mode in which they are called; others save the caller's mode and
>run rounding to nearest; still others simply run in whatever mode
>they're called.  Check the documentation.
>
>The arrangement and values of the rounding mode bits are inspired by the
>most widely used architecture, iA.
>  
>  // Constants and methods in java.lang.Math
>  public static final int TONEAREST  = 0x00000000;
>  public static final int UPWARD     = 0x08000000;
>  public static final int DOWNWARD   = 0x04000000;
>  public static final int TOWARDZERO = 0x0C000000;
>  public static final int ROUNDMASK  = 0x0C000000;
>  public static int getRound();
>  public static void setRound(int round);
>
>RealJava and IEEE 754
>=====================
>* The standard requires that results be rounded under program
>  control.  RealJava supports the four types of rounding, using
>  a dynamic mode setting.

I'm not thrilled by this proposal, but I am least thrilled by
the way IEEE promotes dynamic control of rounding modes, and
how that dynamic control is reflected in this proposal.  I've
never understood why anyone would want to use this feature; if
I write a piece of code in "Java", I don't want to have to worry
about what rounding modes might be in effect when someone decides
to call it.  The possibility of 4 different rounding modes
complicates both proof (as if anyone proved anything correct
anymore, but we can pretend) and testing.  True, those people
who care about reproducible behavior (aka "run anywhere") could
guard against this by always setting rounding modes to default,
but this means that extra work is required to get the sensible,
expected behavior, and that is wrong.  (The proposal also lacks
a name for the default Java rounding mode -- there should be
something there with a name like JAVA_DEFAULT_ROUNDING).

Having the rounding modes exist as some sort of a dynamic flag
also adds expense to exception-handling (the same problem
occurs with setjmp/longjmp in C, I don't know if anyone bothers
to get this right); if I write:

  try {
     int x = JAVA_DEFAULT_ROUNDING;
     setRound(x);
     foo();
  } catch (fooException x) {
     // In my un-humble opinion, getRound() had better be equal to x,
     // no matter what "foo" did.
  }

If control of rounding modes is truly necessary, why not either (a)
make its control lexically scoped, and not dynamic or (b) simply
introduce a package loaded with static methods (that could be
inlined and/or specially expanded by a competent compiler) to
give the desired operations.  For instance,

  package IEEE754;
  class Rtz {
     public static double plus(double a, double b);
     public static double minus(double a, double b);
     ...
  }

and thus round-to-zero inner product might look like this:

  import IEEE754.Rtz;

  double innerProductRTZ(double[]x, double[]y) {
     double sum = 0.0;
     for (int i = 0; i < x.length; i++) {
        sum = Rtz.plus(sum,Rtz.times(x[i],y[i]));
     }
  }

Certainly, this is what I would expect to see at the byte code level,
even if it looked more like rounding were a lexically scoped property
at the source language level.  Use of lexical scoping would provide
a way around the icky syntax.  Again, a competent compiler can simply
propagate around the rounding mode setting like any other optimizable
operation, and presumably (on a machine with rounding flags, like most
of them) set the flags before the loop began and reset them after it
was all done.

I'm also a little uneasy about the floating point "exceptions" -- why
not map them onto Java exceptions?  They would not accumulate the way
that some people might like, but the behavior would be more Java-like,
and would still be usable:

double[] x, y;
for (int i=0; i
Received: by validgh.com (SMI-8.6/SMI-SVR4)
	id KAA25575; Mon, 7 Jul 1997 10:43:30 -0700
Received: from terminator.be.com by validgh.com (SMI-8.6/SMI-SVR4)
	id KAA25570; Mon, 7 Jul 1997 10:43:16 -0700
Received: from mail.be.com (be.be.com [207.113.215.1]) by terminator.be.com (8.6.12/8.6.12) with SMTP id KAA01486 for ; Mon, 7 Jul 1997 10:44:24 -0700
Received: from 207.113.215.253 by mail.be.com with smtp
	(Smail3.1.29.1 #1) id m0wlHpj-000o7fC; Mon, 7 Jul 97 10:44 PDT
Message-ID: <33C13972.474@be.com>
Date: Mon, 07 Jul 1997 10:46:10 -0800
From: "Jerome T. Coonen" 
X-Mailer: Mozilla 3.0 (Macintosh; I; 68K)
MIME-Version: 1.0
To: David Chase 
CC: Numeric Interest 
Subject: Re: A proposal for RealJava
References: <3.0.1.32.19970705154810.00b6b8c0@world.std.com>
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Sender: owner-numeric-interest
Precedence: bulk
Reply-To: "Jerome T. Coonen" 
content-length: 3108

David Chase wrote, re. the recent RealJava proposal:

 >> <...many detailed comments about IEEE rounding modes and
 >> exceptions at the language level...>
 >> 
 >> But basically, no, I do not like this proposal (that includes the
parts
 >> I did not comment on, fused operations and extended
representations).
 >> As an implementor, it only adds more work (I've still got to do all
 >> that tricky slow stuff to get it right for Pure Java) and will only
 >> fragment the market, and subvert write-once-run-anywhere. 
 >> 
 >> Enquiring minds want to know:
 >> 
 >>   what is wrong with C++?
 >>   what is wrong with Fortran?
 >>   why not just let Java be what it was intended to be?
 >> 
 >> There are other choices that probably have everything that you want
 >> except for Java's bandwagon -- Modula-3, for instance, has garbage
 >> collection, "objects", generics, better array support than Java,
 >> and it has a generally available "standard interface" that provides
 >> IEEE support pretty much like what was described in Jerome Coonen's
 >> proposal.

David,

Thanks for your comments.

I would respond, briefly, by referring to the desiderata stated at
the start of the document.  The point is to get

 * decent expression evaluation across all platforms, and
 * minimal IEEE-754 conformance

It doesn't seem like much to ask, and for some programs it buys a
lot.  I packaged the proposal as a superset of Pure Java and with
minimal language impact because even discussing changes to Java
is such a delicate topic.

The library-function interface to the IEEE modes and flags is
drawn directly from C9x and other prior practice.  I'm not
opposed to constructs tied more tightly to Java syntax.

Although you didn't say this, one should not construe the
RealJava proposal as an attempt to create the numerical
programmer's paradise.  It's merely one way to make "Java"
commercially viable.

Here's another alternative: PDP-Java on Pentiums, in which the fpu
is set to round results to 53 bits, the registers are treated
as doubles, and all evaluation is to double, a la the PDP-11.

PDP-Java is fast on Pentium and about as accurate as Pure Java,
but it introduces anomalies depending on whether intermediate
values spill from the registers; spilled values are stored as
doubles, so they lose extended range.  As one of a host of
examples, an expression like

   z = (x*y) / (x*y);

can lead to zero, one, infinity, or NaN, depending on x, y,
and which products spill.  PDP-Java is fast, usually
accurate, and in most respects not far from Pure Java, but
it's so anomalous that it's no step forward.

The demand for speed is so great that it's difficult to believe
that vendors of platforms with extended intermediates or fused
mul-add won't be forced to implement one or another impure
"Java" to exploit the capabilities of their platform.
I wrote the RealJava proposal in the belief that the
performance penalties of Pure Java are too onerous and
that a design offering fast, portable code with essential IEEE
features is a better choice than lower-overhead schemes like 
PDP-Java.

-Jerome Coonen