Skip to content
This repository has been archived by the owner on Oct 10, 2019. It is now read-only.

Editorial: How should the final spec deal with mathematical values, Numbers and Integers? #10

Closed
littledan opened this issue Feb 18, 2017 · 26 comments · Fixed by tc39/ecma262#1135

Comments

@littledan
Copy link
Member

Algorithm Conventions is very clear that operations in the spec are on mathematical values where not otherwise noted:

Mathematical operations such as addition, subtraction, negation, multiplication, division, and the mathematical functions defined later in this clause should always be understood as computing exact mathematical results on mathematical real numbers, which unless otherwise noted do not include infinities and do not include a negative zero that is distinguished from positive zero. Algorithms in this standard that model floating-point arithmetic include explicit steps, where necessary, to handle infinities and signed zero and to perform rounding. If a mathematical operation or function is applied to a floating-point number, it should be understood as being applied to the exact mathematical value represented by that floating-point number; such a floating-point number must be finite, and if it is +0 or -0 then the corresponding mathematical value is simply 0.

Right now, the ES specification seems to freely, implicitly coerce between mathematical values and Numbers. I don't think there's any particular place where we leak a Number which is not already in IEEE 754 double range to user code, but I'm not sure if this pattern is tenable for a world where we have an integer type as well.

For example, Array.prototype.indexOf takes a Number as an argument, calls ToInteger to get another Number, does arithmetic like "Let k be len + n." in the mathematical domain, and then calls ToString, which operates on Numbers but not arbitrary mathematical values. Finally, -1 is returned, presumably as a Number

I'm concerned this won't extend well to a world with multiple numeric types. If ToString appends n, which ToString operation is invoked when you call it on a mathematical value, as Array.prototype.indexOf does? Operations like ToInteger will throw on BigInts, but not if it gets invoked on a mathematical value. The -1 that's returned at the end needs to keep returning a Number; no one reading the spec should think it that it might be OK to return a BigInt.

This doesn't have to be changed immediately, but we need to figure this out for integrating the text into the main spec. Maybe this is a non-issue, and I'm being excessively pedantic; I'd be interested in your input, either way. A couple possibilities I see:

  • Clarification that, unless otherwise stated, mathematical values cast implicitly back and forth with Numbers, whereas BigInts get special shout-outs (the minimal, conservative option).
  • A distinct spec type for mathematical values, which you can do spec-level + on, distict from Numbers and BigInts, where you would apply language-level operations like +, with explicit conversions between the two.
  • Removing exact mathematical values from the spec language, and instead always doing everything explicitly in the BigInt or Number domain, with notational conventions to make it efficient to write.

Whatever we choose here will have an impact on spec embedders like HTML and WebIDL, so they need to be included in the discussion.

cc @claudepache @allenwb @domenic @jmdyck @bterlson

@jmdyck
Copy link
Contributor

jmdyck commented Feb 18, 2017

Personally, I prefer option 2 (a distinct spec type for mathematical values).

Option 1 would probably have the least effect on the spec text, but I dislike implicit casts.
Option 3 (removing exact mathematical values) would probably be difficult to do completely.

@domenic
Copy link
Member

domenic commented Feb 21, 2017

I have always found the ES spec's implicit coercions here frustrating and confusing. I would definitely support moving toward something more explicit, although it sounds like a lot of work.

One example where this bit us recently is whatwg/webidl#306. (Web IDL is not the JS spec but its algorithms are written in the same style.) Another fun one was whatwg/streams@c9d7b9d, since although it was implicitly stated in the spec that the total queue size was a JS Number, the consequences of this (namely that a is not always equal to a + b - b) were not fully appreciated and internalized.

Ideally, I'd suggest separate typographical conventions (e.g. 1 vs. 1n vs. 1) for Number vs. BigInt vs. math. Maybe separate operators even (um, + vs. +n vs. +??). And explicit abstract operations for converting between them, and algorithm steps that call such operations.

However, I'm not sure in what cases mathematical numbers are ever actually used in the spec in an important way, that could not be replaced by JS Numbers. So

Removing exact mathematical values from the spec language, and instead always doing everything explicitly in the BigInt or Number domain, with notational conventions to make it efficient to write.

sounds fairly reasonable to me.

@littledan littledan changed the title How should the final spec deal with mathematical values, Numbers and BigInts? Editorial: How should the final spec deal with mathematical values, Numbers and BigInts? Feb 23, 2017
@bterlson
Copy link
Member

I also support cleaning this up (and share people's confusion here). Option 2 seems most reasonable to me but likely the most amount of work. Option 3 seems also doable but I'd want to hear from experts on the status quo before going that route.

@littledan littledan changed the title Editorial: How should the final spec deal with mathematical values, Numbers and BigInts? Editorial: How should the final spec deal with mathematical values, Numbers and Integers? Mar 14, 2017
@claudepache
Copy link

For example, Array.prototype.indexOf takes a Number as an argument, calls ToInteger to get another Number, does arithmetic like "Let k be len + n." in the mathematical domain, and then calls ToString, which operates on Numbers but not arbitrary mathematical values. Finally, -1 is returned, presumably as a Number

The way I read, is that ToInteger (and ToInt32, ToLength, etc.) returns a real number, and the algorithm of Array.prototype.of is correct.

However, there are certainly many domain errors creeping in the spec. I’ve just noted the following in ToIndex: step 2d should not invoke SameValueZero(integerIndex, index), because it does not compare ECMAScript values, but mathematical real numbers.

@claudepache
Copy link

claudepache commented Mar 18, 2017

  • A distinct spec type for mathematical values, which you can do spec-level + on, distict from Numbers and BigInts, where you would apply language-level operations like +, with explicit conversions between the two.

There is also Option 0, namely just be careful to distinguish between a Number (an ECMAScript value of type Number) and a real number (a mathematical concept defined outside ECMAScript), as the current spec text attempts to do. (And kill any accidental implicit conversion, of course.)

Anyway, a quite problematic issue with the current spec is that ECMAScript Numbers and real numbers are typographically very similar: see the subtle difference between “1” and “1”. Such similarity encourages confusion. I propose to use:

  • 0, 1, ∞ — a mathematical number;
  • Number(+0), Number(1), +Infinity (instead of +0, 1, +∞) — an ECMAScript value of type Number;
  • Integer(0), Integer(1) — an ECMAScript value of type Integer;

@jmdyck
Copy link
Contributor

jmdyck commented Mar 18, 2017

There is also Option 0, namely just be careful to distinguish between a Number (an ECMAScript value of type Number) and a real number (a mathematical concept defined outside ECMAScript), as the current spec text attempts to do. (And kill any accidental implicit conversion, of course.)

To me, that sounds pretty much the same as Option 2. Can you draw a distinction between them?

@claudepache
Copy link

claudepache commented Mar 18, 2017

To me, that sounds pretty much the same as Option 2. Can you draw a distinction between them?

For me, a “spec type” is a concept defined for the spec, inside the spec (more precisely in Section 6 ECMAScript Data Types and Values), and has no meaning outside it (unless through an explicit reference to ECMA-262, of course). By contrast, concepts like “real number”, “algorithm”, etc. are defined outside ECMA-262. There is no point to redefine mathematics inside the spec. We just need to say that we use them by mentioning them in Section 5 Notational Conventions (as it is the case today).

@jmdyck
Copy link
Contributor

jmdyck commented Mar 18, 2017

Ah, okay. Well, I didn't think option 2 included redefining mathematics inside the spec. @littledan?

@littledan
Copy link
Member Author

@jmdyck The idea of option 2 was to leave spec math semantically as is. Option 3 is more radical and makes all existing algorithms use different types.

@domenic I like the idea of not overloading + in the spec, if we go with option 3 (option 2 would not have overloading as + would always be used on mathematical values). One variant would be to say that spec + always operates on doubles, and call out to abstract operations like Integer::add to do Integer arithmetic.

@domenic
Copy link
Member

domenic commented Mar 18, 2017

The more I think about this the more excited I get about option 3, FWIW. As long as someone else is signing up to do the work :)

@annevk
Copy link
Member

annevk commented Mar 23, 2017

The implication of option 3 is that int/long/long long and such in IDL would effectively be converting to and from BigInt on the IDL boundary and implementations could optimize to smaller types on the C++/Rust side of things? I like the idea of only having two numeric types both in the language and in standards (implementations will likely have plenty, just as they have for strings).

@littledan
Copy link
Member Author

@annevk What do you mean about IDL? I was imagining that we wouldn't have int, long, or long long do anything with respect to Integer, and instead make a new IDL type for Integer conversions.

@annevk
Copy link
Member

annevk commented Mar 23, 2017

@littledan currently if JavaScript passes in a Number to a long IDL returns an integer of sorts. If we instead standardize on Number and BigInt as numeric types for standards that integer of sorts would be a BigInt, I was assuming.

@littledan
Copy link
Member Author

littledan commented Mar 23, 2017

I think we shouldn't change what existing usages of long long do. In particular, it would probably break code if long long as return value became an Integer. Numbers and Integers cannot be used together--you're supposed to always know which one you have, and do appropriate optimizations on them. It's unfortunate to have cruft, but we can use a new type for cases where we actually want an Integer, without suddenly changing all the existing IDL bindings.

We could allow some sort of overloading for arguments, though. I'd suggest that that be based on explicit overloads, though, so as to not confuse things.

@annevk
Copy link
Member

annevk commented Mar 23, 2017

I think you're still misunderstanding me. When the specification algorithm returns a BigInt to something that returns a long at the IDL layer, obviously that would become a Number again.

@brabalan
Copy link

For the record, this is something we struggled with in JSCert and JSExplain. Any clarification would be most helpful.

@littledan
Copy link
Member Author

@annevk I think a specification algorithm which is going through IDL to output a long long should be written to return a Number; if it puts an Integer there, that seems like it would make things harder to read.

@annevk
Copy link
Member

annevk commented Mar 24, 2017

@littledan I'll try one more time, you have JavaScript -> IDL translation layer -> specification algorithm for the method/attribute/constructor/... -> IDL translation layer -> JavaScript. It seems that the innermost bit should get a BigInt for long long and other integer variants with IDL translating to and from that from Number.

@littledan
Copy link
Member Author

Oh, sure, the specification could be said to be using Integers internally. The broad change has the hypothetical potential to change the meaning of spec text (e.g. if we say spec / on Integer rounds), but I bet the impact would be small in practice and not result in anything in the real world breaking.

@annevk
Copy link
Member

annevk commented Mar 26, 2017

I think that'd be a good change and I'm in favor of adopting the Number/BigInt types and their corresponding conventions throughout other standards too. (We have use cases for integer division, e.g., the Encoding Standard uses it. Currently explicitly called out, but if it could build upon primitives so much the better.)

@jmdyck
Copy link
Contributor

jmdyck commented Mar 26, 2017

The broad change has the hypothetical potential to change the meaning of spec text (e.g. if we say spec / on Integer rounds)

Out of curiosity and possible eventual usefulness, I decided to look into this particular example.

I count 5 uses of the / operator in spec algorithms, and one of ÷. In all cases, the operands are integers, though it's not always clear whether they're integer-valued Numbers or mathematical integers.

In 4 cases, the result of the division is (in general) not an integer (although in 3 of those cases, the result is immediately passed to floor):

  • UTF16Encoding:
    Let cu1 be floor((cp - 0x10000) / 0x400) + 0xD800.
  • MakeDay:
    Let ym be y + floor(m / 12).
  • Array.prototype.reverse:
    Let middle be floor(len/2).
  • Number.prototype.toFixed:
    ... an integer for which the exact mathematical value of n ÷ 10^f - x is as close to zero as possible.

And in 2 cases, the result is an integer:

  • Date.prototype.getTimezoneOffset:
    Return (t - LocalTime(t)) / msPerMinute.
    (As far as I can tell, real-world offsets are always an integral number of minutes.)

  • TypedArray:
    Set O.[[ArrayLength]] to newByteLength / elementSize.
    (The algorithm guarantees that newByteLength is a multiple of elementSize.)

So it turns out that for the 5 uses of / in algorithms, if we said / had round-down semantics, it wouldn't make a normative difference.

Mind you, there are also non-algorithmic (but normative) uses of / for which it would make a difference. E.g., the value of Math.atan2(1,0) is an implementation-dependent approximation to +π/2, and you definitely don't want to be rounding that division down to 1.

@annevk
Copy link
Member

annevk commented Mar 28, 2017

@jmdyck if we get special integer syntax, such as "2n", it seems that π/2 would not be problematic as it would not use integer division by definition (we'd have to rewrite some of the other examples though).

@jmdyck
Copy link
Contributor

jmdyck commented Aug 4, 2017

In his comment in issue #63, @littledan says "I believe we'll work towards a specification with no mathematical values and instead all numeric values in spec-land being either Numbers or BigInts", i.e. option 3.

How is that working out? It seems to me that it would be difficult to completely eliminate mathematical values from the spec.

@littledan
Copy link
Member Author

My current plan is:

  • Make an editorial PR to ecma262 to replace all mathematical values with Numbers
  • When this spec lands, add BigInts as a second numeric type to be available in the JS spec

@jmdyck Do you think this plan is unrealistic? Do you have a place in mind where it will be difficult to use Numbers?

@jmdyck
Copy link
Contributor

jmdyck commented Aug 4, 2017

One place would be in the definition of Numbers itself, e.g.

The remaining 9007199254740990 (that is, 2^53-2) values are denormalized, having the form
s × m × 2^e
where s is +1 or -1, m is a positive integer less than 2^52, and e is -1074.

If s, m, and e aren't mathematical values, then you get an infinite regress of Numbers.


Another spot is in the definition of the phrase “the Number value for x”, and also at all of its uses: "x represents an exact nonzero real mathematical quantity (which might even be an irrational number such as π)".


And I'm not sure how you'd convert NumericLiteral and StringNumericLiteral to Number without going through mathematical values.


There may be other places; I haven't done an exhaustive search.

@littledan
Copy link
Member Author

@jmdyck Oh, sure, you're right that those sorts of infinite regress cases, where you're actually constructing a Number, need to appeal to mathematical values. What if we get rid of all the cases except for those, and explicitly mark those with copious use of the phrase "the mathematical value x"?

littledan added a commit to littledan/ecma262 that referenced this issue Mar 9, 2018
This patch changes math in the ECMAScript specification to be based on
a concrete division into mathematical values and Numbers. All
numeric values and operations are explicitly either one or the other
with this patch. Previously, math took place in mathematical values,
based on a system of implicit conversions between these values and
Numbers.

Closes tc39/proposal-bigint#10
littledan added a commit to littledan/ecma262 that referenced this issue Mar 10, 2018
This patch changes math in the ECMAScript specification to be based on
a concrete division into mathematical values and Numbers. All
numeric values and operations are explicitly either one or the other
with this patch. Previously, math took place in mathematical values,
based on a system of implicit conversions between these values and
Numbers.

Closes tc39/proposal-bigint#10
littledan added a commit to littledan/ecma262 that referenced this issue Mar 10, 2018
This patch changes math in the ECMAScript specification to be based on
a concrete division into mathematical values and Numbers. All
numeric values and operations are explicitly either one or the other
with this patch. Previously, math took place in mathematical values,
based on a system of implicit conversions between these values and
Numbers.

Closes tc39/proposal-bigint#10
littledan added a commit to littledan/ecma262 that referenced this issue Mar 11, 2018
This patch changes math in the ECMAScript specification to be based on
a concrete division into mathematical values and Numbers. All
numeric values and operations are explicitly either one or the other
with this patch. Previously, math took place in mathematical values,
based on a system of implicit conversions between these values and
Numbers.

Closes tc39/proposal-bigint#10
littledan added a commit to littledan/ecma262 that referenced this issue May 1, 2018
This patch changes math in the ECMAScript specification to be based on
a concrete division into mathematical values and Numbers. All
numeric values and operations are explicitly either one or the other
with this patch. Previously, math took place in mathematical values,
based on a system of implicit conversions between these values and
Numbers.

Closes tc39/proposal-bigint#10
littledan added a commit to littledan/ecma262 that referenced this issue Jul 10, 2018
This patch changes math in the ECMAScript specification to be based on
a concrete division into mathematical values and Numbers. All
numeric values and operations are explicitly either one or the other
with this patch. Previously, math took place in mathematical values,
based on a system of implicit conversions between these values and
Numbers.

Closes tc39/proposal-bigint#10
caiolima pushed a commit to caiolima/ecma262 that referenced this issue Apr 15, 2019
This patch changes math in the ECMAScript specification to be based on
a concrete division into mathematical values and Numbers. All
numeric values and operations are explicitly either one or the other
with this patch. Previously, math took place in mathematical values,
based on a system of implicit conversions between these values and
Numbers.

Closes tc39/proposal-bigint#10
caiolima pushed a commit to caiolima/ecma262 that referenced this issue Jun 6, 2019
This patch changes math in the ECMAScript specification to be based on
a concrete division into mathematical values and Numbers. All
numeric values and operations are explicitly either one or the other
with this patch. Previously, math took place in mathematical values,
based on a system of implicit conversions between these values and
Numbers.

Closes tc39/proposal-bigint#10
caiolima pushed a commit to caiolima/ecma262 that referenced this issue Jun 14, 2019
This patch changes math in the ECMAScript specification to be based on
a concrete division into mathematical values and Numbers. All
numeric values and operations are explicitly either one or the other
with this patch. Previously, math took place in mathematical values,
based on a system of implicit conversions between these values and
Numbers.

Closes tc39/proposal-bigint#10
ljharb pushed a commit to littledan/ecma262 that referenced this issue Jun 26, 2019
This patch changes math in the ECMAScript specification to be based on
a concrete division into mathematical values and Numbers. All
numeric values and operations are explicitly either one or the other
with this patch. Previously, math took place in mathematical values,
based on a system of implicit conversions between these values and
Numbers.

Closes tc39/proposal-bigint#10

Additional commits:
 - Markup: fix 2 well-formedness errors
 - "numeric" -> "mathematical"
     You set up the dichotomy between Numbers and mathematicals, surely the f/v distinction is the same dichotomy.
 - in "the number value that is", change "number" -> "Number"
 - delete "the Number value of"
     Elsewhere, you changed "the Number value of the Element Size" to just "the Element Size". Here's a couple you missed.
 - remove space between operator and subscript
 - insert "the mathematical value of" in a few places
 - insert "<sub>v</sub>" in a few places
 - Re-work a step.
     One problem is that
         the Number value of X &times; Y
     is ambiguous -- it could mean:
         (the Number value of X) &times; Y
     or:
         the Number value of (X &times; Y)

     Another problem is that _n_ is mathematical, so in 10<sup>_n_</sup>, 10 should be mathematical too.
     And then, depending on how the above ambiguity resolves, you'd have to either convert that to a Number or change &times; to a mathematical operator.

     It's simpler (I think) to do all the arithmetic in the math realm, and then convert just the result to a Number.

     (I'm assuming that 'plus' and 'times' as *words* are mathematical operators.)
 - fix some typos in the definition of "integer"
 - Editorial: Switch from f and v to F and R
 - Editorial: Specify that general phrases mean Number
 - Editorial: "Number value of" -> "Number value for"
 - Conversion of mathematical values to Number values is already described in "The Number Type", and is denoted by the phrase "the Number value for X".
 - Editorial: insert "the Number value for" in a few spots (no implicit conversions!)
 - Editorial: reword "the number whose value is MV of |NumericLiteral|"
     The phrase:
         "the number whose value is MV of |NumericLiteral|"
     could be interpreted as:
         "the Number value for MV of |NumericLiteral|"
     but this would be incorrect, because "the Number value for" (6.1.6) doesn't completely describe the rounding that is applied to the MV when obtaining the Number represented by |NumericLiteral|.

     Instead, change to:
         "the Number value represented by |NumericLiteral|"
     which leaves MV out of it, and so refers to 11.8.3's whole process for arriving at a Number value.
 - Fixing some NITs on equations
 - Fixing some issues on text and removing redundant subscripts from operations of some equations
ljharb pushed a commit to littledan/ecma262 that referenced this issue Jul 4, 2019
This patch changes math in the ECMAScript specification to be based on
a concrete division into mathematical values and Numbers. All
numeric values and operations are explicitly either one or the other
with this patch. Previously, math took place in mathematical values,
based on a system of implicit conversions between these values and
Numbers.

Closes tc39/proposal-bigint#10

Additional commits:
 - Markup: fix 2 well-formedness errors
 - "numeric" -> "mathematical"
     You set up the dichotomy between Numbers and mathematicals, surely the f/v distinction is the same dichotomy.
 - in "the number value that is", change "number" -> "Number"
 - delete "the Number value of"
     Elsewhere, you changed "the Number value of the Element Size" to just "the Element Size". Here's a couple you missed.
 - remove space between operator and subscript
 - insert "the mathematical value of" in a few places
 - insert "<sub>v</sub>" in a few places
 - Re-work a step.
     One problem is that
         the Number value of X &times; Y
     is ambiguous -- it could mean:
         (the Number value of X) &times; Y
     or:
         the Number value of (X &times; Y)

     Another problem is that _n_ is mathematical, so in 10<sup>_n_</sup>, 10 should be mathematical too.
     And then, depending on how the above ambiguity resolves, you'd have to either convert that to a Number or change &times; to a mathematical operator.

     It's simpler (I think) to do all the arithmetic in the math realm, and then convert just the result to a Number.

     (I'm assuming that 'plus' and 'times' as *words* are mathematical operators.)
 - fix some typos in the definition of "integer"
 - Editorial: Switch from f and v to F and R
 - Editorial: Specify that general phrases mean Number
 - Editorial: "Number value of" -> "Number value for"
 - Conversion of mathematical values to Number values is already described in "The Number Type", and is denoted by the phrase "the Number value for X".
 - Editorial: insert "the Number value for" in a few spots (no implicit conversions!)
 - Editorial: reword "the number whose value is MV of |NumericLiteral|"
     The phrase:
         "the number whose value is MV of |NumericLiteral|"
     could be interpreted as:
         "the Number value for MV of |NumericLiteral|"
     but this would be incorrect, because "the Number value for" (6.1.6) doesn't completely describe the rounding that is applied to the MV when obtaining the Number represented by |NumericLiteral|.

     Instead, change to:
         "the Number value represented by |NumericLiteral|"
     which leaves MV out of it, and so refers to 11.8.3's whole process for arriving at a Number value.
 - Fixing some NITs on equations
 - Fixing some issues on text and removing redundant subscripts from operations of some equations
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants