-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Conversation
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.
Questions I don't know the answer to:
|
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? |
Separately: Yes, mathematical values are used pervasively in the spec, see 5.2 Algorithm Conventions. |
Which tests cover this change? |
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 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. |
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 π. 😄 |
@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. |
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.
@littledan while that spec section says:
while in Math.PI:
There is no issue with "the Number value", the issue is with Wouldn't it make sense to define the concrete value of |
@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> |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: "reprented" => "represented"
There was a problem hiding this comment.
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)
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. |
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:
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. |
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. ;) |
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. |
54ee2b8
to
9a8e138
Compare
For reference, most of Java's (There being so many JVM implementations is why I mentioned it.) |
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. |
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. |
This patch further specifies Math library functions which are defined
as implementation-defined approximations. The functions are given
restricted ranges of accuracy:
accurate within one ulp
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.