Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Normative: Specify accuracy of numeric functions #903

Closed
wants to merge 2 commits into from

Conversation

littledan
Copy link
Member

@littledan littledan commented Apr 24, 2017

This patch further specifies Math library functions which are defined
as implementation-defined approximations. The functions are given
restricted ranges of accuracy:

  • Trigonometric functions, logarithms, Math.pow and Math.hypot are specified
    accurate within one ulp
  • Math.sqrt is defined to have correct rounding

Based on revised test262 tests, JSC, ChakraCore, SpiderMonkey and V8
all seem to implement these semantics already.

Specifying Math functions was discussed at the July 2014 and May 2015
TC39 meetings; this patch attempts to capture the conclusion of that
discussion.

This patch further specifies Math library functions which are defined
as implementation-defined approximations. The functions are given
restricted ranges of accuracy:
- Trigonometric functions, logarithms and Math.hypot are specified
  accurate within one ulp
- Math.sqrt is defined to have correct rounding

Based on revised test262 tests, JSC, ChakraCore, SpiderMonkey and V8
all seem to implement these semantics already.

Specifying Math functions was discussed at the July 2014 and May 2015
TC39 meetings; this patch attempts to capture the conclusion of that
discussion.
@littledan littledan added normative change Affects behavior required to correctly evaluate some ECMAScript source text web reality labels Apr 24, 2017
@littledan
Copy link
Member Author

Questions I don't know the answer to:

  • Should we have greater accuracy specified for pow, logarithms, or hypot?
  • Is saying "1 ulp" kosher in spec-land? (It's not like we say anywhere what "sine" itself means, or bitwise | for that matter.) Should it have a definition in algorithm conventions, or be rephrased entirely?

@ljharb
Copy link
Member

ljharb commented Apr 24, 2017

I think ulp is much less common than sine or bitwise; I think a definition in terms of epsilon would be massively helpful.

Separately, is "mathematical value" defined elsewhere in the spec?

@littledan
Copy link
Member Author

Separately: Yes, mathematical values are used pervasively in the spec, see 5.2 Algorithm Conventions.

@jfbastien
Copy link

Based on revised test262 tests, JSC, ChakraCore, SpiderMonkey and V8 all seem to implement these semantics already.

Which tests cover this change?

@leobalter
Copy link
Member

This is still imprecise for test262. Some of the precision tests were removed due to the abstract use of approximation. e.g.: https://github.com/tc39/test262/pull/995/files#diff-8534023e5b7a49de95399aa7edebbfc0R5

Ideally, the spec text should be clear that Math.PI is approximately 3.1415926535897932 means it's not only approximately that value, but it's accurately represented but that given value.

If we only say something is an approximation of a value, it is being subjective to any approximation, that means 3.14 is reasonable as well.

I'm +1 to include this clear definition of representation value.

@anba
Copy link
Contributor

anba commented Apr 24, 2017

Ideally, the spec text should be clear that Math.PI is approximately 3.1415926535897932 means it's not only approximately that value, but it's accurately represented but that given value.

It somewhat depends on how the spec text for Math.PI is supposed to be understood: Is 3.1415926535897932 an approximation for the Number value of π or an approximation for π itself? You could also argue that the latter case is the only correct interpretation, because Number value of x is never an approximation, but always an exact number, which means "approximately" can only refer to π. 😄

@littledan
Copy link
Member Author

@jfbastien See this PR: tc39/test262#995 .

@leobalter I actually don't see any ambiguity in the spec text. "The Number value for π" is well-defined, see the definition of "the Number value" in this section. Presumably, the phrase "which is approximately 3.1415926535897932" has been non-normative all along. cc @allenwb maybe you could clarify the intention here.

@anba
Copy link
Contributor

anba commented Apr 24, 2017

Does fdlibm provide precise enough implementations to guarantee a 1 ulp error bound for these functions?

These constants are redundant with text already present.
The approximation is well-specified by "the Number value of".
Added a <dfn> for that phrase to clarify.
@leobalter
Copy link
Member

leobalter commented Apr 24, 2017

@littledan while that spec section says:

In this specification, the phrase “the Number value for x” where x represents an exact nonzero real mathematical quantity (which might even be an irrational number such as π) means a Number value chosen in the following manner. Consider the set of all finite values of the Number type, with -0 removed and with two additional values added to it that are not representable in the Number type, namely 21024 (which is +1 × 253 × 2971) and -21024 (which is -1 × 253 × 2971). Choose the member of this set that is closest in value to x. If two values of the set are equally close, then the one with an even significand is chosen; for this purpose, the two extra values 21024 and -21024 are considered to have even significands. Finally, if 21024 was chosen, replace it with +∞; if -21024 was chosen, replace it with -∞; if +0 was chosen, replace it with -0 if and only if x is less than zero; any other chosen value is used unchanged. The result is the Number value for x. (This procedure corresponds exactly to the behaviour of the IEEE 754-2008 “round to nearest, ties to even” mode.)

while in Math.PI:

The Number value for π, the ratio of the circumference of a circle to its diameter, which is approximately 3.1415926535897932.

There is no issue with "the Number value", the issue is with which is approximately 3.1415926535897932.

Wouldn't it make sense to define the concrete value of Math.PI defined within the constraints of "the Number value"?

@littledan
Copy link
Member Author

@leobalter It looks to me like the sentence is redundant, with one part specifying an exact answer, and the other part saying, "and, that value is approximately ...". So I think we should make things more clear that the second half is non-normative. I uploaded a second patch in this PR to do that, and a patch in the test262 PR to check for the constants' exact value.

<p>This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *false* }.</p>
<emu-note>`Math.LN10` can be accurately reprented by the ECMAScript Number literal `2.302585092994046`.</emu-note>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/reprented/represented/g

ECMAScript seems meta here. Can we only use represented by 2.302585092994046 or at least represented by the Number 2.302585092994046?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In anyway, I like this change. It solves the ambiguity issue for me.

<p>This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *false* }.</p>
<emu-note>`Math.E` can be accurately reprented by the ECMAScript Number literal `2.7182818284590452354`.</emu-note>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "reprented" => "represented"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Number literal" => "numeric literal" (https://tc39.github.io/ecma262/#sec-static-semantics-mv)

@rossberg
Copy link
Member

rossberg commented Apr 25, 2017

Is 1 ULP a realistic requirement? The last few times I remember this topic being discussed the conclusion was that efficient numeric algorithms cannot guarantee that for the whole input range -- for very large arguments the error will grow much larger.

Moreover, you want to make sure that you don't install a requirement that prevents implementations from using actual hardware instructions to implement these functions. AFAICT, their precision also tends to vary more.

In general, FP numerics (and especially transcendentals) is a very complicated and subtle topic. While I very much agree with the goal of having more spec rigor, I would be very concerned about just dropping idealistic requirements into the spec without some serious consulting from experts on the subject. Last time this came up at TC39 that was the consensus, too. Unfortunately, AFAIK nobody ever reached out to find such experts.

@littledan
Copy link
Member Author

Well, I definitely don't want to specify it this way if it's not achievable in reasonable software. I based the 1 ulp requirement on, from the fdlibm readme:

FDLIBM is intended to provide a reasonably portable (see
assumptions below), reference quality (below one ulp for
major functions like sin,cos,exp,log) math library
(libm.a).

However, we have more here than just those major functions. I'm not sure where to find further documentation about the accuracy of the functions; the source code has comments documenting the algorithm, but I don't see where the accuracy is documented. Maybe the functions that are not those four and not sqrt should have unspecified precision.

From the notes it looks like in 2014 we sought an expert, and in 2015 Dan Gohman came as that expert. However, we didn't get as far as talking about how accurate things could be, as we got hung up on V8's behavior at the time, which was less accurate (and is now more accurate). @sunfishcode any more thoughts?

I think it would be a reasonable decision to say that we want accuracy, even if that means sacrificing the ability to use less accurate hardware units. IIRC Dan pointed out in the 2015 meeting that you can use that hardware to get a ballpark, and approximate more detailed answers from there. Maybe we should leave it to WebAssembly to provide the fastest, least portable version, and require an accurate, portable version in JavaScript. V8 has switched to provide greater accuracy and portability in this way, at the expense of potential performance.

@rossberg
Copy link
Member

Even if FDLIBM meets the bar, is it healthy to lock every implementation in to effectively a single existing library?

Nit re WebAssembly: so far, the goal has been to be more portable than JavaScript. ;)

@littledan
Copy link
Member Author

I doubt there's only one library in the world providing this sort of accuracy; more likely is that there are many others which don't come with a permissive free license. I don't think this would be like standardizing on ICU.

So, let's look around for an expert and delay this at least until we get that review.

@dead-claudia
Copy link

For reference, most of Java's java.lang.Math methods are spec'd to be within 1 ulp, with exception of Math.atan2 (2 ulps) and the hyperbolic trig functions (2.5 ulps). They are also spec'd to be semi-monotonic except for Math.hypot, where it only needs to be semi-monotonic iff one parameter is held constant.

(There being so many JVM implementations is why I mentioned it.)

@sunfishcode
Copy link
Member

I don't presently know enough to make an adequate recommendation.

Unfortunately, there isn't a clear consensus for the definition of ULP. Everyone agrees on what it means in most cases, but there are corner cases, in particular around values close to integral powers of 2, where different definitions give different answers, and there isn't a single definition which is unambiguously best. I myself don't have a recommendation for TC39 at this time.

Another complication is that, as observed above, many implementations don't clearly document their error bounds, so it's not easy to survey implementations to determine worst-case bounds.

I can at least contribute this observation: in ad-hoc non-exhaustive observations, I have seen Darwin's libm's sin/cos, which is what some implementations use to implement Math.sin/Math.cos, be off by more than 1 ULP, assuming CRlibm is correct. For example, for sin(0x1.fffffffffea6dp+1023), CRlibm returned -0x1.ef3e5d92c3009p-3 while Darwin's libm returned -0x1.ef3e5d92c3006p-3. It wouldn't be surprising if other popular libm implementations have similar or even somewhat greater errors. Consequently, the proposal here would require changes in such implementations.

@littledan
Copy link
Member Author

Thanks for the informative responses, @isiahmeadows and @sunfishcode ! Seems like this patch doesn't really make sense without a bunch more detailed study; we can leave things open-ended for now.

@littledan littledan closed this Jun 5, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
normative change Affects behavior required to correctly evaluate some ECMAScript source text web reality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants