Thoughts on IEEE P754
Waldemar Horwat wrote:
Some tidbits about our embedding of decimal:
- Contagion should be towards decimal if decimal and binary are mixed as operands. 5.3m + 1 should be 6.3m, not 6.3. If we use 128-bit decimal, this also makes the behavior of heterogeneous comparisons (binary compared to decimal) sensible.
What should 5.3m + 1.0000000000000001 produce?
I also don't understand the heterogeneous comparisons comment. What should 1.0000000000000001 == 1.0000000000000001m produce?
- We should not print the trailing zeroes. IEEE P754 does not require it, and it breaks things such as array lookup. There is precedence for this in ECMAScript: -0 and +0 both print as "0". If someone really wants to distinguish among equal numbers, he can do it with a function call to one of the mandated functions.
Please explain the breakage?
When dealing with currency, having 0.05 + 0.05 produce 0.10 is a feature.
- Sam Ruby
On Thu, Aug 21, 2008 at 9:08 PM, Sam Ruby <rubys at intertwingly.net> wrote:
Waldemar Horwat wrote:
- We should not print the trailing zeroes. IEEE P754 does not require it, and it breaks things such as array lookup. There is precedence for this in ECMAScript: -0 and +0 both print as "0". If someone really wants to distinguish among equal numbers, he can do it with a function call to one of the mandated functions.
Please explain the breakage?
In ES3, an expression like a[b] calculates ToObject(a) and ToString(b), then performs the property lookup. Thus arr[0] and arr["0"] are identical in meaning.
bclary.com/2004/11/07/#a-11.2.1
Assuming we retain this rule and add decimal numbers, a question arises as to how this code should behave:
var a = []; a[0] = "first"; a[0.0m] = "second"; // a second property? a[0.000m] = "third"; // a third?
The question applies to all objects, not just Arrays.
I think this is a tricky question; neither answer is obviously right to me.
Waldemar wrote:
I just had some time to spend reading the proposed draft of the new floating point spec. It quickly became apparent that ES3.1 is far out of conformance with that draft and it would be a significant undertaking to update it to conform to the new standard. In addition, doing so would introduce breaking incompatibilities into the language.
Not necessarily -- see below. IEEE 754-2008 does not mandate how a language should conform, and in particular it does not, in general, mandate that the 754 behavior be the default.
The discussion of whether ES should attempt to become a conforming implementation is a good one, however!
Here is a smattering of major issues. The chapter and section numbers refer to the draft IEEE P754:
- Many, if not most, of the mandatory functions in chapter 5 are not implemented in ES3.1. There are a lot that we'd also need to implement on binary floating-point numbers.
This is true.
- IEEE P754 5.12.1 mandates that (decimal or binary) -0 print as "-0". This is not going to happen.
It only requires that there be a function which converts to and from a string which preserves (among other things) the sign of a zero. (For example, there could be a 'toIEEEstring' function, just as Java has an IEEEremainder static method in the Math class.)
- IEEE P754 5.12 mandates that binary floating-point numbers convert exactly to a string format with a hexadecimal mantissa and back. An implementation must provide these conversions.
This is just one of the mandatory functions in Clause 5 already mentioned. This one is trivial because this is the hexadecimal string format in C99, so C compilers should already provide this.
A few other tidbits about IEEE P754:
- There are many different ways of representing the same decimal number, which the standard calls a cohort. Some numbers, such as zeroes, have thousands of different variants within the cohort. Fortunately, printing a decimal number is not required to indicate which member of a cohort is chosen. This avoids problems for us such as having the numbers x == y but a[x] and a[y] silently referring to different array elements (which is what would happen if we printed 5.00m differently from 5.m).
As with -0, one is not required to preserve the zeros by default, but I would strongly recommend that it is the default behavior (it is required that there be some function that does preserve the zeros). IEEE 754-2008 requires that it be possible to convert a decimal number to a string and back again and end up with the same sign, significand, and exponent as the original number.
For why this is important, see: speleotrove.com/decimal/decifaq1.html#tzeros
(The default convert-decimal-to-string could/should preserve the sign of zeros, too, as this won't break anything.)
- The standard now supports signed NaN's which are distinguishable from each other in various ways (total order predicate, isSignMinus, etc.). There has been some talk about deferring to the IEEE spec instead of listing the permissible binary floating-point values explicitly as was done in ES3, but we must maintain something like the existing language to avoid getting into this quagmire.
This is an interesting one. IEEE 754-1985 really had its head in the sand on the sign of NaNs; anything that could look at the encoding of a binary floating-point number could detect the sign of a NaN (even though it had no meaning other than for diagnostics). However, the copy operations such as abs and negate (optional and ill-defined in 754-1985 but now required) define sign manipulations, and it was agreed that these manipulations should be predictable whether the value is a NaN or not. Hence 754-2008 had to tighten up the definition of the sign of a NaN in several ways (but it is still undefined after most operations -- for example, hardware might, and often does, XOR the signs during a multiply, regardless of the remaining bits of the encoding).
- Implementors of the standard could not agree on the binary representation of decimal numbers. There are two mutually incompatible ways of representing them -- the mantissa can be either a binary integer or an array of 10-bit groups, each representing three decimal digits. I can't tell whether they behave differently in some scenarios -- if the format is wide enough, the integral representation can represent more values, and their handling of noncanonicals differs.
They both describe an identical set of values, so an implementation could use either. We use the decimal encoding because it's faster in software (and almost certainly in hardware, too) because decimal rounding is so much simpler with a decimal encoding -- see speleotrove.com/decimal/decperf.html. All the hardware implementations use the decimal encoding.
- Some parts of IEEE P754 are ambiguous. For example, the notion of a subnormal is unclear when applied to decimal numbers. It appears that you can have two equal values x and y of the same decimal format where x == y, x is subnormal, but y is not subnormal.
Not so:
2.1.51 subnormal number: In a particular format, a non-zero floating-point number with magnitude less than the magnitude of that format's smallest normal number
and:
2.1.38 normal number: For a particular format, a finite non-zero floating-point number with magnitude greater than or equal to a minimum b^emin value, where b is the radix.
(where ^ indicates superscript). emin is 1-emax, and emax is part of the definition of a format and is a constant for a format.
(Also, underflow is more strictly defined for decimal than for binary. We were unable to clear up the messiness there for binary floating-point.)
Some tidbits about our embedding of decimal:
- Contagion should be towards decimal if decimal and binary are mixed as operands. 5.3m + 1 should be 6.3m, not 6.3. If we use 128-bit decimal, this also makes the behavior of heterogeneous comparisons (binary compared to decimal) sensible.
Yes, this could work. To answer Sam's later question:
What should 5.3m + 1.0000000000000001 produce?
this would convert the binary constant (which will have been rounded to 1 on conversion from the string to double) to decimal, so the result would be 6.3m.
It doesn't really help with comparisons, for example, comparing 0.1 to 0.1m would still compare false. (The double representation of 0.1 is exactly 0.1000000000000000055511151231257827021181583404541015625, which when converted to a decimal128 is 0.1000000000000000055511151231257827.)
- We should not print the trailing zeroes. IEEE P754 does not require it, and it breaks things such as array lookup. There is precedence for this in ECMAScript: -0 and +0 both print as "0". If someone really wants to distinguish among equal numbers, he can do it with a function call to one of the mandated functions.
(See comment above.)
Mike
Unless stated otherwise above: IBM United Kingdom Limited - Registered in England and Wales with number 741598. Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU
On Fri, Aug 22, 2008 at 2:30 AM, Jason Orendorff <jason.orendorff at gmail.com> wrote:
On Thu, Aug 21, 2008 at 9:08 PM, Sam Ruby <rubys at intertwingly.net> wrote:
Waldemar Horwat wrote:
- We should not print the trailing zeroes. IEEE P754 does not require it, and it breaks things such as array lookup. There is precedence for this in ECMAScript: -0 and +0 both print as "0". If someone really wants to distinguish among equal numbers, he can do it with a function call to one of the mandated functions.
Please explain the breakage?
In ES3, an expression like a[b] calculates ToObject(a) and ToString(b), then performs the property lookup. Thus arr[0] and arr["0"] are identical in meaning.
bclary.com/2004/11/07/#a-11.2.1
Assuming we retain this rule and add decimal numbers, a question arises as to how this code should behave:
var a = []; a[0] = "first"; a[0.0m] = "second"; // a second property? a[0.000m] = "third"; // a third?
The question applies to all objects, not just Arrays.
I think this is a tricky question; neither answer is obviously right to me.
Understood. And I understand that Waldemar would prefer that trailing zeros be dropped.
It seems to me that if trailing zeros are retained in a call to ToString, then to be consistent with ES3, the three assignments above would produce three properties. And if trailing zeros are lost, you would expect to only have one. Both approaches are internally consistent, and it surprises me to see either one characterized as "breaking" array lookup.
Like Mike, I would argue that toString on decimal quantities preserve trailing zeros.
-j
- Sam Ruby
On Fri, Aug 22, 2008 at 6:22 AM, Sam Ruby <rubys at intertwingly.net> wrote:
On Fri, Aug 22, 2008 at 2:30 AM, Jason Orendorff <jason.orendorff at gmail.com> wrote:
var a = []; a[0] = "first"; a[0.0m] = "second"; // a second property? a[0.000m] = "third"; // a third?
[...]
It seems to me that if trailing zeros are retained in a call to ToString, then to be consistent with ES3, the three assignments above would produce three properties. And if trailing zeros are lost, you would expect to only have one. Both approaches are internally consistent, and it surprises me to see either one characterized as "breaking" array lookup.
Well, the intuition is that two equal numbers should index the same property. This is an invariant in ES3 and it makes a lot of sense.
Jason Orendorff wrote:
On Fri, Aug 22, 2008 at 6:22 AM, Sam Ruby <rubys at intertwingly.net> wrote:
On Fri, Aug 22, 2008 at 2:30 AM, Jason Orendorff <jason.orendorff at gmail.com> wrote:
var a = []; a[0] = "first"; a[0.0m] = "second"; // a second property? a[0.000m] = "third"; // a third? [...]
It seems to me that if trailing zeros are retained in a call to ToString, then to be consistent with ES3, the three assignments above would produce three properties. And if trailing zeros are lost, you would expect to only have one. Both approaches are internally consistent, and it surprises me to see either one characterized as "breaking" array lookup.
Well, the intuition is that two equal numbers should index the same property. This is an invariant in ES3 and it makes a lot of sense.
Correct me if I'm wrong, but the intuition would more properly be based on string equality, and not numeric equality. As an example, how many properties would the following produce, with an conformant implementation of ES3?
var a = []; a['0.0'] = "first"; a[0.0] = "second"; // a second property a['0'] = "third"; // replaces second property
Intuition is a very tricky thing. Place the following in front of any third grader, and tell me what they will write below the line.
1 . 0 5
- 2 . 0 5
- Sam Ruby
var a = []; a[0] = "first"; a[0.0m] = "second"; // a second property? a[0.000m] = "third"; // a third?
[...]
It seems to me that if trailing zeros are retained in a call to ToString, then to be consistent with ES3, the three assignments above would produce three properties. And if trailing zeros are lost, you would expect to only have one. Both approaches are internally consistent, and it surprises me to see either one characterized as "breaking" array lookup.
Well, the intuition is that two equal numbers should index the same property. This is an invariant in ES3 and it makes a lot of sense.
Seems to me the problem here is not decimal (or binary) representations but arrays (consider a(0.0) and a("0.0")).
In effect the array definition in ES3 says -- in a rather roundabout way -- that all indexes must be an integer (which fits in 32 bits if encoded in binary). So, as long as there's a toInt32 in there, decimals with trailing zeros after the point are just fine. In other words the processing of array indexes could (conceptually) be toString->toNumber->toInt32 (or something like that).
Mike
Unless stated otherwise above: IBM United Kingdom Limited - Registered in England and Wales with number 741598. Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU
On 22 Aug 2008, at 15:22, Jason Orendorff wrote:
On Fri, Aug 22, 2008 at 6:22 AM, Sam Ruby <rubys at intertwingly.net>
wrote:On Fri, Aug 22, 2008 at 2:30 AM, Jason Orendorff <jason.orendorff at gmail.com> wrote:
var a = []; a[0] = "first"; a[0.0m] = "second"; // a second property? a[0.000m] = "third"; // a third?
[...]
It seems to me that if trailing zeros are retained in a call to ToString, then to be consistent with ES3, the three assignments above would produce three properties. And if trailing zeros are lost, you would expect to only have one. Both approaches are internally consistent, and it surprises me to see either one characterized as "breaking" array lookup.
Well, the intuition is that two equal numbers should index the same property. This is an invariant in ES3 and it makes a lot of sense.
It's not quite an invariant in ES3. See my earlier message on the
subject:
drj
Mike Cowlishaw wrote:
Seems to me the problem here is not decimal (or binary) representations but arrays (consider a(0.0) and a("0.0")).
In effect the array definition in ES3 says -- in a rather roundabout way -- that all indexes must be an integer (which fits in 32 bits if encoded in binary). So, as long as there's a toInt32 in there, decimals with trailing zeros after the point are just fine. In other words the processing of array indexes could (conceptually) be toString->toNumber->toInt32 (or something like that).
That's incompatible with ES3. There is no toInt in there.
Waldemar
Jason Orendorff wrote:
On Fri, Aug 22, 2008 at 6:22 AM, Sam Ruby <rubys at intertwingly.net> wrote:
On Fri, Aug 22, 2008 at 2:30 AM, Jason Orendorff <jason.orendorff at gmail.com> wrote:
var a = []; a[0] = "first"; a[0.0m] = "second"; // a second property? a[0.000m] = "third"; // a third? [...]
It seems to me that if trailing zeros are retained in a call to ToString, then to be consistent with ES3, the three assignments above would produce three properties. And if trailing zeros are lost, you would expect to only have one. Both approaches are internally consistent, and it surprises me to see either one characterized as "breaking" array lookup.
Well, the intuition is that two equal numbers should index the same property. This is an invariant in ES3 and it makes a lot of sense.
Two values that are === should index the same property [*]. Two values that are == do not, in general.
So, it would be possible to make === take into account trailing zeroes, while == ignores them. Given that the trailing zeroes are supposed to be semantically significant ("scale preservation"), there's a strong case for saying that values that differ only by trailing zeroes should be distinguishable -- and if not by ===, then how?
One can argue about the merits or otherwise of scale preservation, but it's an intentional property of the P754 decimal floating point design. See section 2 of speleotrove.com/decimal/IEEE-cowlishaw-arith16.pdf
for the arguments in favour (which I'm not sure I buy into fully).
[*] but not conversely: two values that index the same property are not necessarily ===.
Sam Ruby wrote:
Waldemar Horwat wrote:
Some tidbits about our embedding of decimal:
- Contagion should be towards decimal if decimal and binary are mixed as operands. 5.3m + 1 should be 6.3m, not 6.3. If we use 128-bit decimal, this also makes the behavior of heterogeneous comparisons (binary compared to decimal) sensible.
What should 5.3m + 1.0000000000000001 produce?
I also don't understand the heterogeneous comparisons comment. What should 1.0000000000000001 == 1.0000000000000001m produce?
Depends. There are many decimal formats. Which decimal format and representation are you specifying?
Waldemar
Sam Ruby wrote:
- We should not print the trailing zeroes. IEEE P754 does not require it, and it breaks things such as array lookup. There is precedence for this in ECMAScript: -0 and +0 both print as "0". If someone really wants to distinguish among equal numbers, he can do it with a function call to one of the mandated functions.
Please explain the breakage?
It's already explained in the other emails here.
When dealing with currency, having 0.05 + 0.05 produce 0.10 is a feature.
Contrary to some beliefs, the different cohort members in IEEE P754 are not a valid indicator of precision. To prove this, assume that the different cohort members are an indicator of precision. Then evaluate:
3.00 + 1.00000
IEEE P754 returns 4.00000, while the result is actually precise to only two digits after the decimal point. Hence a contradiction. QED.
It gets worse. If you insist on distinguishing cohort members while printing then you'll really confuse users who print the result of:
1m/.1m
Hint: The result is equal to 10 but could not print as "10". Scroll down to see the actual value that distinguishing cohort members would require to be printed.
Answer: Were we to distinguish cohort members, that value of 10 would be required to print as "1e1". This is unacceptable and, as mentioned before, breaks array indexing.
Waldemar
Mike Cowlishaw wrote:
In effect the array definition in ES3 says -- in a rather roundabout way -- that all indexes must be an integer (which fits in 32 bits if encoded in binary).
No, arrays can have arbitrary properties, and there are no additional coercions when a property is accessed on an array. The only differences from other objects are that:
- setting 'length' may have additional side effects;
- if a property that is set is an "array index", then there may be a side effect on 'length'.
See ECMA-262 section 15.4.5.1.
On Fri, Aug 22, 2008 at 2:28 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
Waldemar Horwat wrote:
Some tidbits about our embedding of decimal:
- Contagion should be towards decimal if decimal and binary are mixed as operands. 5.3m + 1 should be 6.3m, not 6.3. If we use 128-bit decimal, this also makes the behavior of heterogeneous comparisons (binary compared to decimal) sensible.
What should 5.3m + 1.0000000000000001 produce?
I also don't understand the heterogeneous comparisons comment. What should 1.0000000000000001 == 1.0000000000000001m produce?
Depends. There are many decimal formats. Which decimal format and representation are you specifying?
Decimal128. But the key to this question is the fact that the binary64 floating point constant is indistinguishable from the value 1.
Waldemar
- Sam RUby
David-Sarah Hopwood wrote:
Jason Orendorff wrote:
On Fri, Aug 22, 2008 at 6:22 AM, Sam Ruby <rubys at intertwingly.net> wrote:
On Fri, Aug 22, 2008 at 2:30 AM, Jason Orendorff <jason.orendorff at gmail.com> wrote:
var a = []; a[0] = "first"; a[0.0m] = "second"; // a second property? a[0.000m] = "third"; // a third? [...]
It seems to me that if trailing zeros are retained in a call to ToString, then to be consistent with ES3, the three assignments above would produce three properties. And if trailing zeros are lost, you would expect to only have one. Both approaches are internally consistent, and it surprises me to see either one characterized as "breaking" array lookup. Well, the intuition is that two equal numbers should index the same property. This is an invariant in ES3 and it makes a lot of sense.
Two values that are === should index the same property [*]. Two values that are == do not, in general.
So, it would be possible to make === take into account trailing zeroes, while == ignores them. Given that the trailing zeroes are supposed to be semantically significant ("scale preservation"), there's a strong case for saying that values that differ only by trailing zeroes should be distinguishable -- and if not by ===, then how?
ES3 already addresses this. Binary floating-point already has a cohort of equal values: {-0, +0}. They are considered === to each other. The same should apply to cohorts of equal decimal values. Anything else would be madness; you'd say that -0m === +0m, but 1e10m - 1e10m !== 0m.
Waldemar
On 22 Aug 2008, at 19:28, David-Sarah Hopwood wrote:
Jason Orendorff wrote:
On Fri, Aug 22, 2008 at 6:22 AM, Sam Ruby <rubys at intertwingly.net>
wrote:On Fri, Aug 22, 2008 at 2:30 AM, Jason Orendorff <jason.orendorff at gmail.com> wrote:
var a = []; a[0] = "first"; a[0.0m] = "second"; // a second property? a[0.000m] = "third"; // a third? [...]
It seems to me that if trailing zeros are retained in a call to ToString, then to be consistent with ES3, the three assignments
above would produce three properties. And if trailing zeros are lost, you would expect to only have one. Both approaches are internally consistent, and it surprises me to see either one characterized as "breaking" array lookup.Well, the intuition is that two equal numbers should index the same property. This is an invariant in ES3 and it makes a lot of sense.
Two values that are === should index the same property [*].
[*] but not conversely: two values that index the same property are not necessarily ===.
That highly desirable, but not true, as my earlier message
illustrates. In ES3 the ToString operator on Numbers is not required
to be a true function in the sense of always yielding the same result
for the same input.
drj
Mike Cowlishaw wrote:
Seems to me the problem here is not decimal (or binary)
representations
but arrays (consider a(0.0) and a("0.0")).
In effect the array definition in ES3 says -- in a rather roundabout
way
-- that all indexes must be an integer (which fits in 32 bits if
encoded
in binary). So, as long as there's a toInt32 in there, decimals with trailing zeros after the point are just fine. In other words the processing of array indexes could (conceptually) be toString->toNumber->toInt32 (or something like that).
That's incompatible with ES3. There is no toInt in there.
I confess it's eight years since I looked at that text, but I wasn't saying there was a toInt32 in ES3. I'm just saying that that could/should be added. Surely there was something about array indexes having to have the same result as though it had a toInt32? Anything else that wouldn't convert to an integer would be 'out of range'?
ES arrays are essentially just like Java arrays -- indexed by integers, zero through length-1?
Mike
Unless stated otherwise above: IBM United Kingdom Limited - Registered in England and Wales with number 741598. Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU
David Jones wrote:
On 22 Aug 2008, at 19:28, David-Sarah Hopwood wrote:
Two values that are === should index the same property [*].
[*] but not conversely: two values that index the same property are not necessarily ===.
That highly desirable, but not true, as my earlier message illustrates.
In ES3 the ToString operator on Numbers is not required to be a true function in the sense of always yielding the same result for the same input.
That's true, but I strongly believe that it is a specification bug that should be fixed for ES3.1 and ES-Harmony.
In practice, AFAIK, all the common JavaScript implementations do have a deterministic ToString operation on numbers (that is, deterministic for each implementation; I don't know whether there are differences between implementations).
In order to ensure implementations all behave the same way I suggest
the note at end of section 9.8.1 be moved to normative status.
I agree.
David-Sarah Hopwood wrote:
One can argue about the merits or otherwise of scale preservation, but it's an intentional property of the P754 decimal floating point design. See section 2 of speleotrove.com/decimal/IEEE-cowlishaw-arith16.pdf for the arguments in favour (which I'm not sure I buy into fully).
Actually, I'm sure that I don't buy them. Waldemar's argument about scale not implying precision is quite convincing. From the above paper:
If the scale is not preserved, measurements and contracts may appear
to be more vague (less precise) than intended, and information is
lost. For example, the length of a beam might be specified as
1.200 meters; if this value is altered to 1.2 meters then a contractor
could be entitled to provide a beam that is within 5 centimeters of
that length, rather than measured to the nearest millimeter.
Similarly, if the scale is altered by computation, it must be
possible to detect that change (even if the value of the result
is exact) so that incorrect changes to algorithms can be readily
detected.
There are two arguments made here, and the first is simply wrong, because it depends completely on scale being an indicator of precision. The second argument (starting from "Similarly,") perhaps has a little more merit, but there is a great risk that the preservation of scale will lead people to incorrectly believe that it indicates precision, and I think that outweighs any other possible benefit.
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 2:28 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
Waldemar Horwat wrote:
Some tidbits about our embedding of decimal:
- Contagion should be towards decimal if decimal and binary are mixed as operands. 5.3m + 1 should be 6.3m, not 6.3. If we use 128-bit decimal, this also makes the behavior of heterogeneous comparisons (binary compared to decimal) sensible. What should 5.3m + 1.0000000000000001 produce?
I also don't understand the heterogeneous comparisons comment. What should 1.0000000000000001 == 1.0000000000000001m produce? Depends. There are many decimal formats. Which decimal format and representation are you specifying?
Decimal128. But the key to this question is the fact that the binary64 floating point constant is indistinguishable from the value 1.
If you're using Decimal128, then 1.0000000000000001 == 1.0000000000000001m is false because the double on the left evaluates to 1. If you're using Decimal64, then 1.0000000000000001 == 1.0000000000000001m is true because both sides evaluate to their respective versions of 1.
Here's a question for you:
What should the full result of converting 1.1 to a Decimal be in ECMAScript? Describe the precise answer and justify your choice of answer. Hint: This would behave remarkably differently depending on whether we settle on Decimal64 or Decimal128.
Waldemar
On Fri, Aug 22, 2008 at 3:55 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 2:28 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
Waldemar Horwat wrote:
Some tidbits about our embedding of decimal:
- Contagion should be towards decimal if decimal and binary are mixed as operands. 5.3m + 1 should be 6.3m, not 6.3. If we use 128-bit decimal, this also makes the behavior of heterogeneous comparisons (binary compared to decimal) sensible. What should 5.3m + 1.0000000000000001 produce?
I also don't understand the heterogeneous comparisons comment. What should 1.0000000000000001 == 1.0000000000000001m produce? Depends. There are many decimal formats. Which decimal format and representation are you specifying?
Decimal128. But the key to this question is the fact that the binary64 floating point constant is indistinguishable from the value 1.
If you're using Decimal128, then 1.0000000000000001 == 1.0000000000000001m is false because the double on the left evaluates to 1. If you're using Decimal64, then 1.0000000000000001 == 1.0000000000000001m is true because both sides evaluate to their respective versions of 1.
And this makes heterogeneous comparisons sensible?
I'm suggesting that 1.0000000000000001 is "about 1" and any operator, be it addition, subtraction, multiplication, division, or even double equals, first "downconverts" values with additional precision before applying the operation.
A few examples:
false: 1.2 - 1.1 == 0.0 false: 1.2m - 1.1 == 0.0 false: 1.2 - 1.1m == 0.0 false: 1.2m - 1.1m == 0.0 false: 1.2 - 1.1 == 0.0m false: 1.2m - 1.1 == 0.0m false: 1.2 - 1.1m == 0.0m true: 1.2m - 1.1m == 0.0m
false
Here's a question for you:
What should the full result of converting 1.1 to a Decimal be in ECMAScript? Describe the precise answer and justify your choice of answer. Hint: This would behave remarkably differently depending on whether we settle on Decimal64 or Decimal128.
At the present time, I am only suggesting that ECMAScript support Decimal128, and that there not by any implicit conversions to Decimal.
Decimal.parse(n) would take n.ToString() and then parse that as a Decimal128 value. If (in the future) additional precisions were allowed by the standard, then there would be a second parameter on Decimal.parse that would allow the precision to be specified. Meanwhile:
true: Decimal.parse(1.1) == 1.1m
Waldemar
- Sam Ruby
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 3:55 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 2:28 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
Waldemar Horwat wrote:
Some tidbits about our embedding of decimal:
- Contagion should be towards decimal if decimal and binary are mixed as operands. 5.3m + 1 should be 6.3m, not 6.3. If we use 128-bit decimal, this also makes the behavior of heterogeneous comparisons (binary compared to decimal) sensible. What should 5.3m + 1.0000000000000001 produce?
I also don't understand the heterogeneous comparisons comment. What should 1.0000000000000001 == 1.0000000000000001m produce? Depends. There are many decimal formats. Which decimal format and representation are you specifying? Decimal128. But the key to this question is the fact that the binary64 floating point constant is indistinguishable from the value
If you're using Decimal128, then 1.0000000000000001 == 1.0000000000000001m is false because the double on the left evaluates to 1. If you're using Decimal64, then 1.0000000000000001 == 1.0000000000000001m is true because both sides evaluate to their respective versions of 1.
And this makes heterogeneous comparisons sensible?
I'm suggesting that 1.0000000000000001 is "about 1" and any operator, be it addition, subtraction, multiplication, division, or even double equals, first "downconverts" values with additional precision before applying the operation.
A few examples:
false: 1.2 - 1.1 == 0.0 false: 1.2m - 1.1 == 0.0 false: 1.2 - 1.1m == 0.0 false: 1.2m - 1.1m == 0.0 false: 1.2 - 1.1 == 0.0m false: 1.2m - 1.1 == 0.0m false: 1.2 - 1.1m == 0.0m true: 1.2m - 1.1m == 0.0m
The last one should, of course, be false, or are you saying that 0.1m == 0.0m?
Here's a question for you:
What should the full result of converting 1.1 to a Decimal be in ECMAScript? Describe the precise answer and justify your choice of answer. Hint: This would behave remarkably differently depending on whether we settle on Decimal64 or Decimal128.
At the present time, I am only suggesting that ECMAScript support Decimal128, and that there not by any implicit conversions to Decimal.
Decimal.parse(n) would take n.ToString() and then parse that as a Decimal128 value. If (in the future) additional precisions were allowed by the standard, then there would be a second parameter on Decimal.parse that would allow the precision to be specified. Meanwhile:
true: Decimal.parse(1.1) == 1.1m
An interesting choice. This produces more sensible results but directly violates IEEE P754 section 5.3.3.
If we're using Decimal64, then there are no major issues. Converting 1.1 do decimal would produce 1.1m, as both you and I desire.
If we're using Decimal128, then IEEE P754 mandates that binary floats be convertible to decimal floats and that the result of the conversion of 1.1 to decimal be 1.100000000000000088817841970012523m.
Hence, a dilemma if we choose Decimal128. Do we obey the standard?
Waldemar
On Fri, Aug 22, 2008 at 4:18 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 3:55 PM, Waldemar Horwat <waldemar at google.com> wrote:
Here's a question for you:
What should the full result of converting 1.1 to a Decimal be in ECMAScript? Describe the precise answer and justify your choice of answer. Hint: This would behave remarkably differently depending on whether we settle on Decimal64 or Decimal128.
At the present time, I am only suggesting that ECMAScript support Decimal128, and that there not by any implicit conversions to Decimal.
Decimal.parse(n) would take n.ToString() and then parse that as a Decimal128 value. If (in the future) additional precisions were allowed by the standard, then there would be a second parameter on Decimal.parse that would allow the precision to be specified. Meanwhile:
true: Decimal.parse(1.1) == 1.1m
An interesting choice. This produces more sensible results but directly violates IEEE P754 section 5.3.3.
I'm looking at DRAFT P754, April 24, 2008. Section 5.3.3 in this draft is entitled "logBFormat operations". I doubt that's what you are referring to. Can you provide me with a bit more context?
If we're using Decimal64, then there are no major issues. Converting 1.1 do decimal would produce 1.1m, as both you and I desire.
If we're using Decimal128, then IEEE P754 mandates that binary floats be convertible to decimal floats and that the result of the conversion of 1.1 to decimal be 1.100000000000000088817841970012523m.
Hence, a dilemma if we choose Decimal128. Do we obey the standard?
Deviations from the standard merit bug reports.
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 4:18 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 3:55 PM, Waldemar Horwat <waldemar at google.com> wrote:
Here's a question for you:
What should the full result of converting 1.1 to a Decimal be in ECMAScript? Describe the precise answer and justify your choice of answer. Hint: This would behave remarkably differently depending on whether we settle on Decimal64 or Decimal128. At the present time, I am only suggesting that ECMAScript support Decimal128, and that there not by any implicit conversions to Decimal.
Decimal.parse(n) would take n.ToString() and then parse that as a Decimal128 value. If (in the future) additional precisions were allowed by the standard, then there would be a second parameter on Decimal.parse that would allow the precision to be specified. Meanwhile:
true: Decimal.parse(1.1) == 1.1m An interesting choice. This produces more sensible results but directly violates IEEE P754 section 5.3.3.
I'm looking at DRAFT P754, April 24, 2008. Section 5.3.3 in this draft is entitled "logBFormat operations". I doubt that's what you are referring to. Can you provide me with a bit more context?
Sorry, I meant 5.4.2.
If we're using Decimal64, then there are no major issues. Converting 1.1 do decimal would produce 1.1m, as both you and I desire.
If we're using Decimal128, then IEEE P754 mandates that binary floats be convertible to decimal floats and that the result of the conversion of 1.1 to decimal be 1.100000000000000088817841970012523m.
Hence, a dilemma if we choose Decimal128. Do we obey the standard?
Deviations from the standard merit bug reports.
Are you saying this is a bug in IEEE P754? I doubt it.
Waldemar
On 22 Aug 2008, at 20:42, David-Sarah Hopwood wrote:
David Jones wrote:
On 22 Aug 2008, at 19:28, David-Sarah Hopwood wrote:
Two values that are === should index the same property [*].
[*] but not conversely: two values that index the same property are not necessarily ===.
That highly desirable, but not true, as my earlier message
illustrates. In ES3 the ToString operator on Numbers is not required to be a true function in the sense of always yielding the same result for the same input.That's true, but I strongly believe that it is a specification bug
that should be fixed for ES3.1 and ES-Harmony.
Well, so do I. But until 3 implementations get on board and say "we
can live with changing the note at the end of section 9.8.1 to being
normative" then it looks like it's Not Gonna Happen.
In practice, AFAIK, all the common JavaScript implementations do
have a deterministic ToString operation on numbers (that is, deterministic
for each implementation; I don't know whether there are differences
between implementations).In order to ensure implementations all behave the same way I suggest
the note at end of section 9.8.1 be moved to normative status.
I agree.
drj
On Fri, Aug 22, 2008 at 11:39 AM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
When dealing with currency, having 0.05 + 0.05 produce 0.10 is a feature.
Contrary to some beliefs, the different cohort members in IEEE P754 are not a valid indicator of precision. To prove this, assume that the different cohort members are an indicator of precision. Then evaluate:
3.00 + 1.00000
IEEE P754 returns 4.00000, while the result is actually precise to only two digits after the decimal point. Hence a contradiction. QED.
This example makes me realize how utterly confused and ignorant I am about the rationale for decimal.
In particular, on one of the ES3.1 calls, Sam explained why he thought mixed mode arithmetic should convert to binary -- IIUC, in order to faithfully represent our ignorance of the precision that should be associated with the result. From what I thought I'd understood, I would have expected the above example to produce 4.00. However, the above example makes clear that decimal by itself, even when non-mixed, does not attempt to accurately (or conservatively) represent one's ignorance of precision.
Sam, did I get your rationale wrong? Now that we've seen these examples from Waldemar, could you try explaining again why mixed should result in binary?
Finally, I'd like to take a poll: Other than people working on decimal at IBM and people on the EcmaScript committee, is there anyone on this list who thinks that decimal adds significant value to EcmaScript? If so, please speak up. Thanks.
On Fri, Aug 22, 2008 at 5:06 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 4:18 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 3:55 PM, Waldemar Horwat <waldemar at google.com> wrote:
Here's a question for you:
What should the full result of converting 1.1 to a Decimal be in ECMAScript? Describe the precise answer and justify your choice of answer. Hint: This would behave remarkably differently depending on whether we settle on Decimal64 or Decimal128. At the present time, I am only suggesting that ECMAScript support Decimal128, and that there not by any implicit conversions to Decimal.
Decimal.parse(n) would take n.ToString() and then parse that as a Decimal128 value. If (in the future) additional precisions were allowed by the standard, then there would be a second parameter on Decimal.parse that would allow the precision to be specified. Meanwhile:
true: Decimal.parse(1.1) == 1.1m An interesting choice. This produces more sensible results but directly violates IEEE P754 section 5.3.3.
I'm looking at DRAFT P754, April 24, 2008. Section 5.3.3 in this draft is entitled "logBFormat operations". I doubt that's what you are referring to. Can you provide me with a bit more context?
Sorry, I meant 5.4.2.
I still don't see the conflict. If we desire, we can provide multiple mechanisms for converting binary64 floating point numbers to decimal128 floating point numbers. At a minimum, we should provide an analog to ES's existing parseFloat. I'd suggest that it be named Decimal.parse instead, and that it make use of decimal128-convertFromDecimalCharacter.
If we're using Decimal64, then there are no major issues. Converting 1.1 do decimal would produce 1.1m, as both you and I desire.
If we're using Decimal128, then IEEE P754 mandates that binary floats be convertible to decimal floats and that the result of the conversion of 1.1 to decimal be 1.100000000000000088817841970012523m.
Hence, a dilemma if we choose Decimal128. Do we obey the standard?
Deviations from the standard merit bug reports.
Are you saying this is a bug in IEEE P754? I doubt it.
I guess I was unclear. Places where draft ES3.1 deviate from IEEE P754 merits bug reports -- on ES3.1.
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 5:06 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 4:18 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 3:55 PM, Waldemar Horwat <waldemar at google.com> wrote:
Here's a question for you:
What should the full result of converting 1.1 to a Decimal be in ECMAScript? Describe the precise answer and justify your choice of answer. Hint: This would behave remarkably differently depending on whether we settle on Decimal64 or Decimal128. At the present time, I am only suggesting that ECMAScript support Decimal128, and that there not by any implicit conversions to Decimal.
Decimal.parse(n) would take n.ToString() and then parse that as a Decimal128 value. If (in the future) additional precisions were allowed by the standard, then there would be a second parameter on Decimal.parse that would allow the precision to be specified. Meanwhile:
true: Decimal.parse(1.1) == 1.1m An interesting choice. This produces more sensible results but directly violates IEEE P754 section 5.3.3. I'm looking at DRAFT P754, April 24, 2008. Section 5.3.3 in this draft is entitled "logBFormat operations". I doubt that's what you are referring to. Can you provide me with a bit more context? Sorry, I meant 5.4.2.
I still don't see the conflict. If we desire, we can provide multiple mechanisms for converting binary64 floating point numbers to decimal128 floating point numbers. At a minimum, we should provide an analog to ES's existing parseFloat. I'd suggest that it be named Decimal.parse instead, and that it make use of decimal128-convertFromDecimalCharacter.
Now we've gone full circle without actually answering the question I asked: What should the full result of converting 1.1 to a Decimal be in ECMAScript?
"An error" or "you can't do it" is not a valid answer because it violates IEEE P754.
Waldemar
On Fri, Aug 22, 2008 at 5:46 PM, Mark S. Miller <erights at google.com> wrote:
On Fri, Aug 22, 2008 at 11:39 AM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
When dealing with currency, having 0.05 + 0.05 produce 0.10 is a feature.
Contrary to some beliefs, the different cohort members in IEEE P754 are not a valid indicator of precision. To prove this, assume that the different cohort members are an indicator of precision. Then evaluate:
3.00 + 1.00000
IEEE P754 returns 4.00000, while the result is actually precise to only two digits after the decimal point. Hence a contradiction. QED.
This example makes me realize how utterly confused and ignorant I am about the rationale for decimal.
I do know that if I have $5 and add a dime, I expect to have $5.10, and not $5.
I do believe that the folks who worked on IEEE P754 worked through a lot of scenarios, and can provide exquisite detail for the rationale for all of these operations.
Yes, I understand that many of these results seem foreign to those of us (myself included) who have been poisoned by years of only having binary64 floating point available to us. I will say that when faced with artificial examples, my intuition tends to fail me until I find corresponding examples using currency.
In particular, on one of the ES3.1 calls, Sam explained why he thought mixed mode arithmetic should convert to binary -- IIUC, in order to faithfully represent our ignorance of the precision that should be associated with the result. From what I thought I'd understood, I would have expected the above example to produce 4.00. However, the above example makes clear that decimal by itself, even when non-mixed, does not attempt to accurately (or conservatively) represent one's ignorance of precision.
When talking about decimal128, I believe that 5 is exact, and 0.10 is exact, and the result of adding the two should also be exact.
When talking about binary64, clearly 5 is also exact, but 0.10 can not be represented exactly.
Sam, did I get your rationale wrong? Now that we've seen these examples from Waldemar, could you try explaining again why mixed should result in binary?
I find it easier to talk about real examples than abstractions. I've done the following quickly, so forgive me if I get some detail wrong.
A binary floating point number has 52 bits of fraction, and by assuming an implicit leading one, they get an additional bit. This means that 1.1 is stored as (for brevity, I'll use hex)
[1].1999999999999
A conversion of that to decimal128 would be equivalent to computing
4953959590107545m / 4503599627370496m
Which would produce
1.099999999999999866773237044981215
Repeating that for 1.2 produces
0x13333333333333L 5404319552844595m / 4503599627370496m 1.199999999999999955591079014993738
With this data, you can compute as well as I can what the various permutations of 1.2 - 1.1 would produce for various combinations of binary64 and decimal128 quantities.
But that being said, I recognize that any choice we make here is a tradeoff. Making binary64 contagious makes life more difficult for those that are attempting to produce a decimal result. Doug's "use decimal" suggestion can mitigate that. As well as providing a Decimal.subtract method with provisions for roundingMode and (perhaps someday) precision helps.
As would providing a Decimal.quantize speleotrove.com/decimal/dnnumb.html#numbquan
js> Decimal.quantize(1.199999999999999955591079014993738m, 0.01)
1.20
Finally, I'd like to take a poll: Other than people working on decimal at IBM and people on the EcmaScript committee, is there anyone on this list who thinks that decimal adds significant value to EcmaScript? If so, please speak up. Thanks.
-- Cheers, --MarkM
- Sam Ruby
Oops, forgot to copy the list
Sam Ruby wrote:
Now we've gone full circle without actually answering the question I asked: What should the full result of converting 1.1 to a Decimal be in ECMAScript?
"An error" or "you can't do it" is not a valid answer because it violates IEEE P754. I'm clearly not following something.
I'm suggesting that there not be any implicit conversions from binary64 to decimal128. I've made a suggestion as to what Decimal.parse should do. If there are no other means to do a conversion, that I can't answer the question about what such a non-existent method might do.
If you suggest that we need to do implicit conversions, I am aware of a few different mechanisms to do so. I think that the one that produces the most expected results would be based on decimal128-convertFromDecimalCharacter.
If we want to provide more explicit conversions, then such named methods should match the definition in IEEE P754.
I don't know how to more fully answer the question.
To comply with IEEE P754, we must provide conversions and they must have the (rather poor for Decimal128) semantics given by that standard.
Waldemar
Sam Ruby wrote:
I find it easier to talk about real examples than abstractions. I've done the following quickly, so forgive me if I get some detail wrong.
A binary floating point number has 52 bits of fraction, and by assuming an implicit leading one, they get an additional bit. This means that 1.1 is stored as (for brevity, I'll use hex)
[1].1999999999999
A conversion of that to decimal128 would be equivalent to computing
4953959590107545m / 4503599627370496m
Which would produce
1.099999999999999866773237044981215
That's incorrect. The correct answer is 1.100000000000000088817841970012523, which is closer mathematically to 1.1.
Repeating that for 1.2 produces
0x13333333333333L 5404319552844595m / 4503599627370496m 1.199999999999999955591079014993738
This one is correct.
Waldemar
On Fri, Aug 22, 2008 at 7:07 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
I find it easier to talk about real examples than abstractions. I've done the following quickly, so forgive me if I get some detail wrong.
A binary floating point number has 52 bits of fraction, and by assuming an implicit leading one, they get an additional bit. This means that 1.1 is stored as (for brevity, I'll use hex)
[1].1999999999999
A conversion of that to decimal128 would be equivalent to computing
4953959590107545m / 4503599627370496m
Which would produce
1.099999999999999866773237044981215
That's incorrect. The correct answer is 1.100000000000000088817841970012523, which is closer mathematically to 1.1.
Apparently, I rounded incorrectly. Rounding the first number correctly produces:
1.199999999999A 4953959590107546m / 4503599627370496m 1.100000000000000088817841970012523
Repeating that for 1.2 produces
0x13333333333333L 5404319552844595m / 4503599627370496m 1.199999999999999955591079014993738
This one is correct.
One ramification of requiring up-conversion to decimal128 of mixed mode operations would mean that the following would be true
(1.1 < 1.1m) && (1.2m < 1.2)
Sam Ruby wrote:
On Fri, Aug 22, 2008 at 7:07 PM, Waldemar Horwat <waldemar at google.com> wrote:
Sam Ruby wrote:
I find it easier to talk about real examples than abstractions. I've done the following quickly, so forgive me if I get some detail wrong.
A binary floating point number has 52 bits of fraction, and by assuming an implicit leading one, they get an additional bit. This means that 1.1 is stored as (for brevity, I'll use hex)
[1].1999999999999
A conversion of that to decimal128 would be equivalent to computing
4953959590107545m / 4503599627370496m
Which would produce
1.099999999999999866773237044981215 That's incorrect. The correct answer is 1.100000000000000088817841970012523, which is closer mathematically to 1.1.
Apparently, I rounded incorrectly. Rounding the first number correctly produces:
1.199999999999A 4953959590107546m / 4503599627370496m 1.100000000000000088817841970012523
Repeating that for 1.2 produces
0x13333333333333L 5404319552844595m / 4503599627370496m 1.199999999999999955591079014993738 This one is correct.
One ramification of requiring up-conversion to decimal128 of mixed mode operations would mean that the following would be true
(1.1 < 1.1m) && (1.2m < 1.2)
That's if we don't do the conversions the way you advocate in the other thread. It's an interesting dilemma.
Double and float arithmetic already has the same property. If you take 1.1f (i.e. the float) and compare it with 1.1 (the double), you'll find that one is less than the other. At least the operators are consistent.
Waldemar
On Aug 22, 2008, at 2:46 PM, Mark S. Miller wrote:
Finally, I'd like to take a poll: Other than people working on decimal at IBM and people on the EcmaScript committee, is there anyone on this list who thinks that decimal adds significant value to EcmaScript? If so, please speak up. Thanks.
I'm on the EcmaScript committee, but I'll answer anyway. My impression
is that decimal support is wildly out of line with the stated goals of
ES3.1 (cleanup and minimal critical enhancements), and was only
brought under consideration because of IBM's naked threat to vote
against any spec that did not include it. In the era of competing
specs, buying IBM's vote was worth a rush job.
Furthermore, even though decimal doesn't seem like a completely
useless feature, I believe it has been given far disproportionate
weight relative to its actually value to the wide range of possible
Web applications, perhaps because it is of particular interest to Web
applications that directly handle money. However, that is either not
present or only a minor part of many of today's popular Web
applications on the public Web.
To my view, Decimal is much less important overall than, say,
ByteArray or some other effective way to represent binary data, which
is not in ES3.1 and was even rejected from ES4 (at least as then
spec'd). There's a lot more popular Web applications that would be
interested in manipulating binary data efficiently than care about
calculating prices.
Finally, while Decimal seems like it will be useful to knowledgeable
developers dealing with financial data, it seems likely to confuse the
average developer more than it will help. At least, a fair amount of
the discussion so far has made me feel confused, and I'd like to think
I have more than the average understanding of machine arithmetic for a
programmer.
Now that we are beyond the era of competing specs and in the era of
Harmony, perhaps we could reconsider the value of buying IBM's vote,
and incorporate enhancements to the ECMAScript number model in a
thoughtful, unrushed way, after appropriate consideration of the
merits and use cases.
, Maciej
On 22 Aug 2008, at 22:46, Mark S. Miller wrote:
Finally, I'd like to take a poll: Other than people working on decimal at IBM and people on the EcmaScript committee, is there anyone on this list who thinks that decimal adds significant value to EcmaScript? If so, please speak up. Thanks.
Yes. I think decimal adds value, but I'm afraid my strongest reason
for thinking so is that decimal is getting added to other languages
(C#, C++, Python) and JavaScript will be perceived as "not as useful/
modern/fashionable" if it doesn't have it and therefore get used less
and therefore have a less healthy community of users.
I have other reasons too, but that's the strongest.
drj
On Sat, Aug 23, 2008 at 7:08 PM, David Jones <drj at ravenbrook.com> wrote:
On 22 Aug 2008, at 22:46, Mark S. Miller wrote:
Finally, I'd like to take a poll: Other than people working on decimal at IBM and people on the EcmaScript committee, is there anyone on this list who thinks that decimal adds significant value to EcmaScript? If so, please speak up. Thanks.
Yes. I think decimal adds value, but I'm afraid my strongest reason for thinking so is that decimal is getting added to other languages (C#, C++, Python) and JavaScript will be perceived as "not as useful/ modern/fashionable" if it doesn't have it and therefore get used less and therefore have a less healthy community of users.
I have other reasons too, but that's the strongest.
drj
forgive me if this is a dumb question, from a dumb user, but is there any significant advantage to using decimal arithmetic other than being able to do more accurate financial calculations? If no, is there any significant advantage to using decimal arthithmetic over using integer arithmetic? Ecmascript seems quite capable of doing integer arithmetic quite well and without any rounding errors, and it's not that difficult to do calculations in cents instead of dollars. In fact I do just that in my own programs.
Is decimal being considered because it's more intuitive? it might be, but if that's the only reason for its existance, aside from politics, then I can't say that it's a feature that I'm hurting for. Plenty of other nicer features to hurt for. If financial calculations are the only reason for its existance, then it seems to me that simpler solutions lie in what an interpreter does with what's already there, rather than try to add new language features.
As I understand, spidermonkey detects if you're only using integers, and internally only uses integers up until the point that a floating point calculation is introduced. Is it anywhere in the realm of possibility to do something similar for numbers with only two decimal places? In my own programs, if I need anything more than two decimal places, I don't care that much at all about the kinds of rounding errors that floating point arithmetic introduces. (notwithstanding uses that I'm not aware of)
Maciej Stachowiak <mjs at apple.com> wrote:
Finally, I'd like to take a poll: Other than people working on decimal at IBM and people on the EcmaScript committee, is there anyone on this list who thinks that decimal adds significant value to EcmaScript? If so, please speak up. Thanks.
I'm on the EcmaScript committee, but I'll answer anyway. My impression is that decimal support is wildly out of line with the stated goals of ES3.1 (cleanup and minimal critical enhancements), and was only brought under consideration because of IBM's naked threat to vote against any spec that did not include it. In the era of competing specs, buying IBM's vote was worth a rush job.
I am very sorry if you got that impression. Our (IBM) customers tell us that this is a minimum critical enhancement. IBM hardware and software products also didn't want to do this (there are always more 'interesting' or 'higher buzz' things to do than fix the basic technology). It's just something that needs to be done, and the less time one spends debating that, the less stressful and less expensive it will be. Decimal arithmetic is needed because almost all of humanity uses decimal arithmetic, and when the computing model matches the human model, fewer 'surprises' occur and the whole issue can then be ignored, because the devices 'just work right'. That's exactly in line with Apple's philosophy, I believe.
Incidentally, the original 754-1985 committee wanted the standard then to use decimal (hence IEEE 854). It's an accident of history, mostly, that one of the drivers of that standard was a chip that was too far along to change from binary to decimal. We're using binary floating-point today largely because of the need to save a few transistors in 1981 -- that's no longer a good enough reason.
Furthermore, even though decimal doesn't seem like a completely useless feature, I believe it has been given far disproportionate weight relative to its actually value to the wide range of possible Web applications, perhaps because it is of particular interest to Web applications that directly handle money. However, that is either not present or only a minor part of many of today's popular Web applications on the public Web.
I wish that were true. In practice, the applications that survive are those that ... make money.
To my view, Decimal is much less important overall than, say, ByteArray or some other effective way to represent binary data, which is not in ES3.1 and was even rejected from ES4 (at least as then spec'd). There's a lot more popular Web applications that would be interested in manipulating binary data efficiently than care about calculating prices.
[No particular comment on this, except to note that scripting languages such as Rexx can manipulate binary data directly, and this turns out to be a minefield. (Big-endian vs. little-endian, etc.) It's much better to go up a level and handle things in network byte order, represented in hex (etc.). This can then be made more performant at a lower level if necessary.]
Finally, while Decimal seems like it will be useful to knowledgeable developers dealing with financial data, it seems likely to confuse the average developer more than it will help. At least, a fair amount of the discussion so far has made me feel confused, and I'd like to think I have more than the average understanding of machine arithmetic for a programmer.
This isn't about machine arithmetic, that's exactly the point. It's about human arithmetic, and machines working in the same way. There are, of course, plenty of places (HPC for example) where performance is all that matters -- and for that, binary/machine arithmetic is fine. But it's alien to most of the inhabitants of this planet. :-)
Now that we are beyond the era of competing specs and in the era of Harmony, perhaps we could reconsider the value of buying IBM's vote, and incorporate enhancements to the ECMAScript number model in a thoughtful, unrushed way, after appropriate consideration of the merits and use cases.
Adding decimal to ECMAScript was proposed -- and agreed -- for future addition in 1998/1999 (see the TC39 minutes from then). We have now appropriately considered that for about ten years, and the IEEE 754 committee (with over 150 contributors) has agreed, after 7+ years of discussion, a very stable decimal arithmetic model. This has been a thoughtful and unrushed process.
Mike
Unless stated otherwise above: IBM United Kingdom Limited - Registered in England and Wales with number 741598. Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU
On Aug 23, 2008, at 6:54 AM, Mike Cowlishaw wrote:
Maciej Stachowiak <mjs at apple.com> wrote:
Finally, I'd like to take a poll: Other than people working on
decimal at IBM and people on the EcmaScript committee, is there anyone on
this list who thinks that decimal adds significant value to EcmaScript?
If so, please speak up. Thanks.I'm on the EcmaScript committee, but I'll answer anyway. My
impression is that decimal support is wildly out of line with the stated goals
of ES3.1 (cleanup and minimal critical enhancements), and was only brought under consideration because of IBM's naked threat to vote against any spec that did not include it. In the era of competing specs, buying IBM's vote was worth a rush job.I am very sorry if you got that impression.
But I don't think you can say the impression is false, because IBM did
in fact make such a threat, which gives the appearance of tainting all
that follows.
Our (IBM) customers tell us that this is a minimum critical enhancement. IBM hardware and
software products also didn't want to do this (there are always more
'interesting' or 'higher buzz' things to do than fix the basic technology). It's
just something that needs to be done, and the less time one spends debating that, the less stressful and less expensive it will be. Decimal arithmetic is needed because almost all of humanity uses decimal arithmetic, and when the computing model matches the human model,
fewer 'surprises' occur and the whole issue can then be ignored, because the devices 'just work right'. That's exactly in line with Apple's philosophy, I believe.
I do agree that binary floating point can be confusing to those who do
not really understand how it works (which is most programmers). But it
is not yet at all clear that having both binary and decimal floating
point will be less confusing.
Incidentally, the original 754-1985 committee wanted the standard
then to use decimal (hence IEEE 854). It's an accident of history, mostly,
that one of the drivers of that standard was a chip that was too far
along to change from binary to decimal. We're using binary floating-point
today largely because of the need to save a few transistors in 1981 --
that's no longer a good enough reason.Furthermore, even though decimal doesn't seem like a completely useless feature, I believe it has been given far disproportionate weight relative to its actually value to the wide range of possible Web applications, perhaps because it is of particular interest to Web applications that directly handle money. However, that is either not present or only a minor part of many of today's popular Web applications on the public Web.
I wish that were true. In practice, the applications that survive are those that ... make money.
Here are a few highly popular Web applications:
- Google search
- Yahoo search
- GMail
- Flickr
- Digg
- Upcoming
- Google Maps
- YouTube
Of these, Flickr is the only one (to my knowledge) that that even
accepts money directly from the user, and even in that case it only
accepts canned currency amounts. I agree there are some much more
commerce-oriented sites that could benefit which are very popular in
their own right, such as Amazon or eBay.
To my view, Decimal is much less important overall than, say, ByteArray or some other effective way to represent binary data, which is not in ES3.1 and was even rejected from ES4 (at least as then spec'd). There's a lot more popular Web applications that would be interested in manipulating binary data efficiently than care about calculating prices.
[No particular comment on this, except to note that scripting
languages such as Rexx can manipulate binary data directly, and this turns out
to be a minefield. (Big-endian vs. little-endian, etc.) It's much better
to go up a level and handle things in network byte order, represented in hex (etc.). This can then be made more performant at a lower level if necessary.]
Manipulating binary data in hex is unlikely to be adequate for many of
the use cases.
Finally, while Decimal seems like it will be useful to knowledgeable developers dealing with financial data, it seems likely to confuse
the average developer more than it will help. At least, a fair amount of the discussion so far has made me feel confused, and I'd like to
think I have more than the average understanding of machine arithmetic
for a programmer.This isn't about machine arithmetic, that's exactly the point. It's
about human arithmetic, and machines working in the same way. There are, of course, plenty of places (HPC for example) where performance is all
that matters -- and for that, binary/machine arithmetic is fine. But it's alien to most of the inhabitants of this planet. :-)
There's nothing human about 10 printing as 1e1 or about the fine
distinction between 1.00m and 1.000000m, or about how .1 without the
magical m suffix might convert to decima as a very surprising valuel,
or about decimal numbers possibly being unusable as array indices.
Now that we are beyond the era of competing specs and in the era of Harmony, perhaps we could reconsider the value of buying IBM's vote, and incorporate enhancements to the ECMAScript number model in a thoughtful, unrushed way, after appropriate consideration of the merits and use cases.
Adding decimal to ECMAScript was proposed -- and agreed -- for future addition in 1998/1999 (see the TC39 minutes from then). We have now appropriately considered that for about ten years, and the IEEE 754 committee (with over 150 contributors) has agreed, after 7+ years of discussion, a very stable decimal arithmetic model. This has been a thoughtful and unrushed process.
I don't think the rules for decimal arithmetic per se seem rushed,
just the process of grafting it on to the JavaScript language seems to
be rushed and full of strange pitfalls. Even JSON (a much technically
simpler and rather well-specified format and technology) took much
longer to design than the timeline expected for decimal.
Maciej
On Aug 23, 2008, at 2:07 AM, Maciej Stachowiak wrote:
To my view, Decimal is much less important overall than, say, ByteArray or some other effective way to represent binary data, which is not in ES3.1 and was even rejected from ES4 (at least as then spec'd).
Just for the record, ByteArray was cut in favor of Vector.<byte>,
which yielded to Vector.<int> with the loss of byte, which yielded
with the loss of int (wait for it ;-)) to the return of ByteVector
(cuts were being made to the troublesome machine integer types, but a
byte-vector type was part of the plan all along). Parameterized types
were ultimately not required; we were back to a nominal type
ByteVector that could be used without new syntax. See
doku.php? id=meetings:minutes_nov_08_2007#minutes bugs.ecmascript.org/ticket/211, bugs.ecmascript.org/ticket/234
Anyway, I agree ByteArray or ByteVector is important. I wouldn't put
it in a lifeboat-ethics contest with Decimal (one of the two must be
tossed over) for ES-Harmony, but for ES3.1 there is no contest.
So I agree that Decimal is out of scope for ES3.1, and I've said so
often, most recently in Oslo. But the hardball being played to get
and let it into ES3.1 has been an obstacle to frank exchange of
opinions among all of the committee members. IMHO.
There's a lot more popular Web applications that would be interested in manipulating binary data efficiently than care about calculating prices.
The ones that calculate prices apparently use pennies and round
carefully when multiplying by things like sales tax rates.
Finally, while Decimal seems like it will be useful to knowledgeable developers dealing with financial data, it seems likely to confuse the average developer more than it will help.
That is unclear. A "use decimal" pragma that does not allow implicit
conversions from binary64 to decimal128 might work well for most
users; they'd never notice the lack of speed or the greater
precision, and they would definitely like the powers-of-five
expressiveness (bugzilla.mozilla.org/show_bug.cgi?id=5856 is
still seeing duplicate reports; bloggers are still blaming one
company or another for "math being broken" in JS). But there are too
many variables to be sure of what would work.
I wrote recently that the best way to get Decimal done is to
implement it as an extension, with operators, I hope with a safe "use
decimal" pragma, in popular open source browsers. Then and only then
might we get some kind of usability feedback. We desperately need
experiments, not guesses or committee prescriptions.
Pushing a de jure spec first, or at the same time as prototype
implementations, and on a short deadline (next spring), risks making
ES3.1 late (again), or -- if the spec makes it by next spring in
advance of usability feedback -- risks premature standardization
(again!).
On Aug 23, 2008, at 6:49 PM, Brendan Eich wrote:
Finally, while Decimal seems like it will be useful to knowledgeable developers dealing with financial data, it seems likely to confuse
the average developer more than it will help.That is unclear. A "use decimal" pragma that does not allow
implicit conversions from binary64 to decimal128
Sorry, I meant to write "... does not allow implicit conversions from
decimal128 to binary64 ...".
FWIW, ByteArray has been immensely popular (and useful) in ActionScript 3. I would hope it can make it into both 3.1 and Harmony.
(ByteVector is probably a better name, though, as its behavior is more Vector-like than Array-like.)
Maciej Stachowiak wrote:
On Aug 22, 2008, at 2:46 PM, Mark S. Miller wrote:
Finally, I'd like to take a poll: Other than people working on decimal at IBM and people on the EcmaScript committee, is there anyone on this list who thinks that decimal adds significant value to EcmaScript? If so, please speak up. Thanks.
I'm on the EcmaScript committee, but I'll answer anyway. My impression
is that decimal support is wildly out of line with the stated goals of
ES3.1 (cleanup and minimal critical enhancements), [...]
I agree. Decimal support is a major additional feature, that seems more in line with the goals of ES-Harmony (if it is added at all), than ES3.1. Let's ditch it from ES3.1.
I just had some time to spend reading the proposed draft of the new floating point spec. It quickly became apparent that ES3.1 is far out of conformance with that draft and it would be a significant undertaking to update it to conform to the new standard. In addition, doing so would introduce breaking incompatibilities into the language.
Here is a smattering of major issues. The chapter and section numbers refer to the draft IEEE P754:
Many, if not most, of the mandatory functions in chapter 5 are not implemented in ES3.1. There are a lot that we'd also need to implement on binary floating-point numbers.
IEEE P754 5.12.1 mandates that (decimal or binary) -0 print as "-0". This is not going to happen.
IEEE P754 5.12 mandates that binary floating-point numbers convert exactly to a string format with a hexadecimal mantissa and back. An implementation must provide these conversions.
A few other tidbits about IEEE P754:
There are many different ways of representing the same decimal number, which the standard calls a cohort. Some numbers, such as zeroes, have thousands of different variants within the cohort. Fortunately, printing a decimal number is not required to indicate which member of a cohort is chosen. This avoids problems for us such as having the numbers x == y but a[x] and a[y] silently referring to different array elements (which is what would happen if we printed 5.00m differently from 5.m).
The standard now supports signed NaN's which are distinguishable from each other in various ways (total order predicate, isSignMinus, etc.). There has been some talk about deferring to the IEEE spec instead of listing the permissible binary floating-point values explicitly as was done in ES3, but we must maintain something like the existing language to avoid getting into this quagmire.
Implementors of the standard could not agree on the binary representation of decimal numbers. There are two mutually incompatible ways of representing them -- the mantissa can be either a binary integer or an array of 10-bit groups, each representing three decimal digits. I can't tell whether they behave differently in some scenarios -- if the format is wide enough, the integral representation can represent more values, and their handling of noncanonicals differs.
Some parts of IEEE P754 are ambiguous. For example, the notion of a subnormal is unclear when applied to decimal numbers. It appears that you can have two equal values x and y of the same decimal format where x == y, x is subnormal, but y is not subnormal.
Some tidbits about our embedding of decimal:
Contagion should be towards decimal if decimal and binary are mixed as operands. 5.3m + 1 should be 6.3m, not 6.3. If we use 128-bit decimal, this also makes the behavior of heterogeneous comparisons (binary compared to decimal) sensible.
We should not print the trailing zeroes. IEEE P754 does not require it, and it breaks things such as array lookup. There is precedence for this in ECMAScript: -0 and +0 both print as "0". If someone really wants to distinguish among equal numbers, he can do it with a function call to one of the mandated functions.
Waldemar