ES Decimal status

Sam Ruby rubys at
Fri Sep 12 12:00:47 PDT 2008


     Poor Pratap.  He's folded in updates from me on the various
     operators several times now.  It has gone from Type(Result(n))
     is Decimal, to Form(Result(n)) is Decimal, and now reads
     Result(n) instanceof Decimal.  I believe that the current
     consensus is that the original formulation was the correct one
     for equality operators, and nearly so for the remaining
     operators.  For arithmetic operators, the correct formulation is
     closer to Type(GetValue(Result(n)) is Decimal, though in most
     cases that simply means that the “if” check moves after the
     existing steps that call GetValue, as it will be those results
     that are checked.

     Before proceeding with what hopefully is a final update, I want
     to verify my assumptions on what the consensus is.  Let me know
     if you have any issues you would like me to add to the list.

Description of ES 3.1m decimal support

     number and decimal are distinct primitive data types.  The
     former is based on IEEE 754 binary 64 floating point, the latter
     is based on IEEE 754r decimal 128 floating point.  Each
     primitive data type is associated with a wrapper type, namely
     Number and Decimal.

     The wrapper classes, when invoked as a function, can be used to
     convert between the two data types.  Conversion from decimal to
     number may involve the loss of precision, and may result in +/-
     infinity if the exponent is outside of the range supported by
     binary64.  Conversion from number to decimal is precise and will
     round trip.

     Mixed mode operations, e.g. the addition of a decimal and a
     number, proceed by first precisely converting the number to a
     decimal, and then by performing a decimal addition operation.
     The notable exception to this is strict equality which will
     always return false if the types of the expressions involve

     Binary Bitwise operators currently defined in terms of producing
     32 bit integer results (e.g. shift), will continue to be defined
     in terms of producing 32 bit integer results, i.e. will produce
     a value of type number.  Such numbers will always convert
     exactly to decimal.

     Additionally, there will be a number of named methods provided
     on the Decimal class.  Many of these methods will accept a math
     context as a final argument.  This will be a simple object,
     which will be examined for a single property named “round”.
     This will be compared against a number of predefined values, and
     will be used to determine the rounding mode of the operation.

     All named decimal methods will convert the default value of the
     arguments directly to decimal, without first converting them to
     a number.  For example '1.1' would be treated as 1.1m when
     passed as an argument to decimal.

     While 128 bit decimal trigonometric functions could be proposed
     at some point in the future, the existing Math libraries are to
     be left untouched, and will continue to return numbers.  This
     means that the following Decimal equivalents will be required:
     abs, min, max.

     While extending base classes is generally frowned upon by many
     in the ES community, Decimal should be extendable in precisely
     the same ways as Number is in ES3.

     In a nutshell, that's pretty much all there is too Decimal

An implementation you can reference

     As opposed to a “reference implementation” :-)

     Often times loose prose such as this doesn't capture nuances as
     well as code or test cases can.  I have published a branch of
     SpiderMonkey with the functionality described above, and those
     that care to can review the source.  This includes a number of
     test cases, and I've published the results for quick reference.
     I've also got a live web service which you can use to evaluate
     simple expressions; for many, this might be easier than
     downloading and building this code yourself.

     Note the recent changes in the typeof, instanceOf, ==, and ===

     I am particularly interested in any test cases people might like
     to see added.  Particularly ones that deal with object-ness or
     prototype chains.

     I have still yet to hear back from from the SquirrelFish team.
     After Redmond, I'm considering adding Decimal support to V8.

Open questions

   * Decimal literals.  I believe that they would be a big win at
     almost a negligible cost. But I fully recognize that no
     backwards incompatible syntax change has been introduced into
     the language to date.

   * Selection of which named methods to include.  Clearly functions
     like quantum and compareTotal are needed.  Not so clear are
     functions that duplicate infix and prefix operators, such as add
     and plus.  I've heard arguments on both sides, and don't have a
     strong opinion on this subject myself.  After we verify
     consensus on the broader approach described in this email, I can
     present a list of potential candidate methods, grouped into
     categories.  We should be able to quickly sort through this
     list.  This effort could be done either on the mailing list or
     in the F2F meeting in Redmond.

   * Whether the named methods are to be static or instance methods.
     I've heard arguments both ways, and could go either way on this.
     Frankly, the infix operators capture the 80% use case.  Instance
     methods feel more OO, and some (like toString and valueOf) are
     required anyway.  Static methods may be more consistent with
     Math.abs, and are asserted to be more suited to code generators
     and optimization, though I must admit that I never could quite
     follow this argument.

   * Should there be a “use decimal”?  To me, this feels like
     something that could be added later.  The fact that all 15 digit
     integers convert exactly, as well as a few common fractions such
     as .5 and .25, greatly reduces the need.  The strategy of doing
     precise conversions also will tend to highlight when mixed
     operations occur, and will do so in a non-fatal way.

Approaches not selected:

   * Decimal as a library only.  Reason: usability concerns and
     evolution of the language concerns.  More specifically, the
     definition of the behaviors of operators like === and + need to
     be specified.  Throwing exceptions would not merely be developer
     unfriendly, it would likely be perceived as causing existing
     libraries to break.  And whatever was standardized would make
     latter support for such operators to be a breaking change – at
     the very least it would require an opt-in.

   * Attempting to round binary 64 values to the nearest decimal 128
     value.  Such approaches are fragile (e.g., 1.2-1.1) and tends to
     hide rather than reveal errors.

   * Decimal being either a “subclass” or “form” of number.  Turned
     out to be too confusing, potentially breaking, and in general
     larger in scope than simply providing a separate type and
     wrapper class.

   * Type(3m) being “object”.  Reason: the only false values for
     objects should be null, and it is highly desirable that 0.0m be
     considered false.

   * Methods naming based on Java's BigDecimal class.  This was my
     original approach as it was initially felt that IEEE 754 was too
     low of level.  This turned out not to be the case, and there are
     some conflicts (e.g. valueOf is a static method with a single
     argument on BigDecimal).

   * Having a separate context class or storing application state in
     Decimal class.  The former is unnecessary namespace pollution in
     a language with a simple syntax for object literals, and the
     latter is against the policy of this working group.

- Sam Ruby

More information about the Es-discuss mailing list