This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 10073 - Problems with the definitions of the trigonometric functions (math:)
Summary: Problems with the definitions of the trigonometric functions (math:)
Status: RESOLVED FIXED
Alias: None
Product: XPath / XQuery / XSLT
Classification: Unclassified
Component: Functions and Operators 3.0 (show other bugs)
Version: Working drafts
Hardware: PC Windows NT
: P2 normal
Target Milestone: ---
Assignee: Michael Kay
QA Contact: Mailing list for public feedback on specs from XSL and XML Query WGs
URL: http://www.w3.org/TR/xpath-functions-...
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-07-03 04:37 UTC by dnovatchev
Modified: 2010-09-21 16:35 UTC (History)
3 users (show)

See Also:


Attachments

Description dnovatchev 2010-07-03 04:37:49 UTC
There are a number of problems with the way trigonometric functions are currently defined in the WD of F&O 1.1:

1. The name of the group of functions. The widely accepted term is "trigonometric" functions, not "trigonometrical" which is used in section 4.7.

2. The table at the start of section 4.7 in its column "Meaning" specifies the range of the three reverse trigonometric functions. However the range of the trigonometric functions (sin(), cos() and tan()) are not specified. Why is this selectivity?

3. The term "range" is used without specifying if this is closed or semi-closed or open range.

4. The summary of 4.7.1 for math:pi() states: "Returns the value of the mathematical constant ð". This is not true. ð is a transcendental number and its value cannot be expressed as a number with finite number of digits (in any numerical system), thus its value cannot be returned. We always work wit more or less *approximation* of pi.

5. Section 4.7.2 math:sqrt states: "Returns the square root of the argument". This is incorrect, because every positive number has *two* square roots -- a positive and a negative one. I guess that what the document meant was to return the positive square root of the argument. Another problem is that sqrt() is not a trigonometric function at all and should not be defined in section 4.7 (this has been filed as a separate bug).

6. Unnecessary limitation for the arguments of math:sin(), math:cos() and math:tan(). In the corresponding sections it is stated that: "If $è is in the range -2ð to +2ð then the result is the xs:double value ·either side of the mathematical sine of the angle; if it is outside this range, then the precision of the result is ·implementation-dependent". It is very unclear how the interval -2ð to +2ð (closed or open) was chosen. There isn't any technical problem of calculating the value of these functions with the same accuracy for any value in the domain of the functions. In case the chosen interval is a hint for a desired method of implementation, then this is not the best hint. It will be cumbersome, repetitive, time consuming and error-prone for the users to always specify their own code that gets the remainder of the argument modulo 2ð.

7. "Either side of". This is defined as: "[Definition] In this section, when the rules for a function say that the returned value must be the xs:double either side of some mathematical quantity, then if the mathematical quantity is precisely representable in the value space of xs:double the exact result must be returned; otherwise it is acceptable to return either the nearest higher xs:double or the nearest lower xs:double, and it is ·implementation-dependent· which of the two is returned". The problem with this is that generally it is challenging to test if a given returned value is really "the either side of" of the value of the mathematical function (typically an irrational number).
Comment 1 Michael Kay 2010-07-03 11:35:37 UTC
It's good to have this feedback. I'll address it in detail in due course. This is the first detailed feedback on the specification of these functions, and I was becoming a bit concerned that my initial drafts would go through "on the nod" without detailed review. I feel I am something of an amateur in this space; I think I was one of the few students who managed to stay awake during David Wheeler's lectures on numerical methods, but it was 40 years ago and I remember painfully little of what we were taught.
Comment 2 Michael Kay 2010-07-05 12:29:54 UTC
1. (trigonometric/trigonometrical) Fixed. My dictionary has both adjectives, but I will accept your advice that the shorter is to be preferred.

2. the entry in the table is a copy of the "Summary" of the function. It's intended to give some useful information about the function without being a complete specification. I think it's useful to say for sin/cos/tan that the argument is in radians, it's not useful for these to say what the range is since there's no doubt about the matter. For asin/acos/atan, it's useful to say that the result is in radians, and since the range is in some sense arbitrary, it's useful to say what range we have chosen.

3. ("range") fixed. (In most cases the limits of the range are some multiple of pi, so it's clearly exclusive; in the one case where one of the bounds was zero, the range is clearly inclusive at that end.)

3. (pi) fixed.

4. (sqrt) fixed. I have also changed the specification so that the square root of negative zero is positive zero as this seems more consistent, but I'm open to persuasion on this.

6, 7. I took input here from the Java and .NET specifications. Java states "The computed result must be within 1 ulp of the exact result. Results must be semi-monotonic". A ulp is defined as "The ulp of a specific real number value is the distance between the two floating-point values bracketing that numerical value". Apart from the requirement to be semi-monotonic, I believe my formulation is equivalent, and that it can be implemented by using the usual iterative-approximation approaches with a sufficiently small epsilon. I believe I also found some material on the precision guaranteed by the .NET methods, but I can't now find it. This input is probably what led me to the idea of offering a guaranteed precision only for inputs in some range; I suspect the original logic was to permit an implementation that started by taking the input value modulo 2*pi, and then getting the nearest value, despite the fact that this two-step approach may increase the error. I agree there's more work needed here.
Comment 3 Henry Zongaro 2010-07-05 13:17:22 UTC
In comment #2, Michael Kay wrote, "4. (sqrt) fixed. I have also changed the specification so that the square root of negative zero is positive zero as this seems more consistent, but I'm open to persuasion on this."

I don't have a copy of IEEE-754 handy, but I'm almost certain that sqrt of negative zero is negative zero according to that standard.  If I'm correct, I don't think it would be a good idea to deviate in this regard without a very strong rationale.
Comment 4 David Carlisle 2010-07-05 13:37:48 UTC
The last public draft of the latest revision of IEEE 754 is

http://www.validlab.com/754R/nonabelian.com/754/comments/Q754.129.pdf

which says:

The squareRoot operation is defined and has a positive sign for all operands ≥0, except that
squareRoot(0) shall be 0.
Comment 5 Michael Kay 2010-07-05 14:08:30 UTC
>The squareRoot operation is defined and has a positive sign for all operands
≥0, except that squareRoot(0) shall be 0.

OK, reverted to that.
Comment 6 Michael Kay 2010-07-05 14:44:34 UTC
While considering this bug report, I would also like to ask the working group to review whether any of the following functions should be added to the list. This is a subset of those specified in section 9.1 ("Recommended functions") of the IEEE 754 (2008) specification:> I have excluded those that can trivially be calculated using other functions.

exp      e^x
exp2     2^x
exp10    10^x

log      log`e x
log2     log`2 x
log10    log`10 x

pow      x ^ n
powr     x ^ y

atan2    atan2(y, x) => atan(y div x) but with cleaner overflow behaviour

sinh, cosh, tanh
asinh acosh, atanh
Comment 7 dnovatchev 2010-07-05 15:01:39 UTC
(In reply to comment #2)

> 6, 7. I took input here from the Java and .NET specifications. Java states "The
> computed result must be within 1 ulp of the exact result. Results must be
> semi-monotonic". A ulp is defined as "The ulp of a specific real number value
> is the distance between the two floating-point values bracketing that numerical
> value". Apart from the requirement to be semi-monotonic, I believe my
> formulation is equivalent, and that it can be implemented by using the usual
> iterative-approximation approaches with a sufficiently small epsilon. I believe
> I also found some material on the precision guaranteed by the .NET methods, but
> I can't now find it. This input is probably what led me to the idea of offering
> a guaranteed precision only for inputs in some range; I suspect the original
> logic was to permit an implementation that started by taking the input value
> modulo 2*pi, and then getting the nearest value, despite the fact that this
> two-step approach may increase the error. I agree there's more work needed
> here.

The reason for raising problem 6. is that the current text will cause the
programmer to always obtain x modulo 2*pi() before passing it as argument,
believing that this will lead to result with increased precision. 

My understanding, confirmed by Michael Kay above, is that most implementations
in practice will always as a first step get the remainder of dividing the
passed argument value by 2*pi(). Therefore, having this done by the programmer
before they call the trigonometric function will duplicate the implementation,
will cause them loss of time, will be error-prone and will decrease the
readability and maintainability of their code.

Due to this reason I propose that the following text should be omitted in all
function definitions:

"If $è is in the range -2ð to +2ð then the result is the xs:double value
·either side of· the mathematical cosine of the angle; if it is outside this
range, then the precision of the result is ·implementation-dependent·."


For problem 7. I simply wonder how the requirement to return "the nearest
higher xs:double or the nearest lower xs:double" can be tested in practice. And
if this requirement most probably won't be tested, then nobody will ever know
whether this requirement has really been fulfilled. Therefore it is necessary
to provide a more easily testable description of the precision of the returned
result.
Comment 8 Michael Kay 2010-07-05 16:32:41 UTC
>For problem 7. I simply wonder how the requirement to return "the nearest
higher xs:double or the nearest lower xs:double" can be tested in practice.

Based on past experience:

(a) Someone will submit a test together with the result that their implementation returns

(b) If any other implementor gets a value that differs from this, they will argue about it until we are satisfied what the correct answer is, and either add a second permitted result or change the expected result accordingly. Because there are only two legitimate results, this process seems quite manageable. Verifying the correct answers needs access to a quad- or variable-precision trig library, there is surely one available somewhere.
Comment 9 dnovatchev 2010-07-05 17:15:06 UTC
(In reply to comment #8)
> >For problem 7. I simply wonder how the requirement to return "the nearest
> higher xs:double or the nearest lower xs:double" can be tested in practice.
> Based on past experience:
> (a) Someone will submit a test together with the result that their
> implementation returns
> (b) If any other implementor gets a value that differs from this, they will
> argue about it until we are satisfied what the correct answer is, and either
> add a second permitted result or change the expected result accordingly.
> Because there are only two legitimate results, this process seems quite
> manageable. Verifying the correct answers needs access to a quad- or
> variable-precision trig library, there is surely one available somewhere.

So, the tester will rely on the correctness of the quad- or variable-precision trig library, but will not prove themselves the correctness? How do we know in the first place that the quad- or variable-precision trig library really produces correct, according to the definition, result(s)? What if it was insufficiently tested or the tests were not too representable?

I am trying to think of a more testable definition of the required precision of the result. Can't it be something like the following:

"If for any given x the decimal fraction of the value of the mathematical function is:

d1 d2 d3 ... dn d(n+1)...

then the returned result must have all of its k decimal digits equal to d1 - dk
and it should be impossible to represent the number .d1 d2 d3 ... dk d(k+1) as a double precision number".

The "mathematical function value" should be taken from reputable sources such as the Wolfram's mathworld site (http://mathworld.wolfram.com/).
Comment 10 Michael Kay 2010-07-05 17:55:25 UTC
>returned result must have all of its k decimal digits

A definition that relies on converting the result to decimal digits seems only to add difficulty; it's better to define the double result without reference to its decimal equivalent. I don't think there's a real problem with the testability of the current definition. For example, consider sin(0.5e0) (for which Java gives 0.479425538604203) 

(a) Wolfram Alpha gives us the exact result 0.4794255386042030002732879352155713880818033679406000675...,

(b) xs:string(xs:double(0.4794255386042030002732879352155713880818033679406000675)) is 0.479425538604203 which is a bit smaller

(c) the rules therefore also allow the next double greater than this, which we can find by translating to the internal form 3fdeaee8744b05f0, adding one to give 3fdeaee8744b05f1, and translating back to a double which displays as the decimal 0.47942553860420306. The two acceptable answers for sin(0.5) are therefore these two xs:double values, so we can write the test as 

sin(0.5) = (xs:double(0.479425538604203), xs:double(0.47942553860420306))

We can write the above as the test query and "true" as the expected result.

Of course the test results can be challenged if anyone believes that either the Wolfram Alpha result or any of the subsequent inferences is wrong.
Comment 11 dnovatchev 2010-07-05 18:36:41 UTC
(In reply to comment #10)
> >returned result must have all of its k decimal digits
> A definition that relies on converting the result to decimal digits seems only
> to add difficulty; it's better to define the double result without reference to
> its decimal equivalent. I don't think there's a real problem with the
> testability of the current definition. For example, consider sin(0.5e0) (for
> which Java gives 0.479425538604203) 
> (a) Wolfram Alpha gives us the exact result
> 0.4794255386042030002732879352155713880818033679406000675...,
> (b)
> xs:string(xs:double(0.4794255386042030002732879352155713880818033679406000675))
> is 0.479425538604203 which is a bit smaller
> (c) the rules therefore also allow the next double greater than this, which we
> can find by translating to the internal form 3fdeaee8744b05f0, adding one to
> give 3fdeaee8744b05f1, and translating back to a double which displays as the
> decimal 0.47942553860420306. The two acceptable answers for sin(0.5) are
> therefore these two xs:double values, so we can write the test as 
> sin(0.5) = (xs:double(0.479425538604203), xs:double(0.47942553860420306))
> We can write the above as the test query and "true" as the expected result.
> Of course the test results can be challenged if anyone believes that either the
> Wolfram Alpha result or any of the subsequent inferences is wrong.

I agree with this example of how testing is to be done correctly. 

Probably the current definition should be ammended to specify at least a hint of a testing approach, adding that the value of the "mathemathical function" is well approximated by well-known reliable resources such as Wolfram Alpha and .. (at least two need to be listed in order to avoid any bias or overreliance).
Comment 12 dnovatchev 2010-07-14 16:29:23 UTC
(In reply to comment #6)
> While considering this bug report, I would also like to ask the working group
> to review whether any of the following functions should be added to the list.
> This is a subset of those specified in section 9.1 ("Recommended functions") of
> the IEEE 754 (2008) specification:> I have excluded those that can trivially be
> calculated using other functions.
> exp      e^x
> exp2     2^x
> exp10    10^x
> log      log`e x
> log2     log`2 x
> log10    log`10 x
> pow      x ^ n
> powr     x ^ y
> atan2    atan2(y, x) => atan(y div x) but with cleaner overflow behaviour
> sinh, cosh, tanh
> asinh acosh, atanh

As I understand, most of these will make it into the next version of the document.

I have three comments:

1. It is well established to denote log`e x as ln x. Please consider using the well-established name.

2. exp2 and exp10 are quite redundant with pow and powr. I think that it would be better if instead of these two functions we have pown m n, where both m and n are positive integers.

3. Why we still don't have or() and and() ? Aren't these much more important than the various trigonometric and exponential functions? Why is this hole in the document?
Comment 13 David Carlisle 2010-07-14 17:07:46 UTC
On 14/07/2010 17:29, bugzilla@jessica.w3.org wrote:

> 1. It is well established to denote log`e x as ln x. Please consider using the
> well-established name.

It seems that many programing languages though use log and log10 rather than ln and log (as used on calculators and some texts) .NET has Math.Log Math.Log10 Fortran has LOG and LOG10, C's math.h the same.
> 
> 2. exp2 and exp10 are quite redundant with pow and powr. I think that it would
> be better if instead of these two functions we have pown m n, where both m and
> n are positive integers.

I'd assume (I think) exp was defined for non integer values?

> 
> 3. Why we still don't have or() and and() ? Aren't these much more important
> than the various trigonometric and exponential functions? Why is this hole in
> the document?

It's a lot easer for the user to define function or(a,b) to be a or b than to define log. So it seems to me clearly more useful for log to be in the standard than or (if only one is in).

David
>
Comment 14 dnovatchev 2010-07-14 17:47:32 UTC
(In reply to comment #13)
> > 2. exp2 and exp10 are quite redundant with pow and powr. I think that it would
> > be better if instead of these two functions we have pown m n, where both m and
> > n are positive integers.
> I'd assume (I think) exp was defined for non integer values?

If so, then exp2 and exp10 are redundant with powr. Though it seems to me that 2^x and 10^x where x is non-integer would be of limited use.

> > 
> > 3. Why we still don't have or() and and() ? Aren't these much more important
> > than the various trigonometric and exponential functions? Why is this hole in
> > the document?
> It's a lot easer for the user to define function or(a,b) to be a or b than to
> define log. So it seems to me clearly more useful for log to be in the standard
> than or (if only one is in).


The question is not what is easier but what is more important. or() and and() are fundamental, necessary and most commonly used. They will need to be used as function objects much more frequently than other functions.

Also, why we have a not() function but we lack or() and and() ?

Following the logic that not() can easily be defined by the user, let us then be consistent and remove not() as one of the XPath functions.

Dimitre
Comment 15 dnovatchev 2010-07-15 01:53:24 UTC
(In reply to comment #12)
> (In reply to comment #6)


> 1. It is well established to denote log`e x as ln x. Please consider using the
> well-established name.

In addition to this, I see that among the proposed functions there is no 
log`x y

It would be best to have this function and also ln x. log2 and log10 are then redundant with log and it would be good to consider whether to have them in the document.

> As I understand, most of these will make it into the next version of the
> document.
> I have three comments:
> 1. It is well established to denote log`e x as ln x. Please consider using the
> well-established name.
> 2. exp2 and exp10 are quite redundant with pow and powr. I think that it would
> be better if instead of these two functions we have pown m n, where both m and
> n are positive integers.
> 3. Why we still don't have or() and and() ? Aren't these much more important
> than the various trigonometric and exponential functions? Why is this hole in
> the document?
Comment 16 Mukul Gandhi 2010-07-15 03:23:31 UTC
(In reply to comment #14)
> Also, why we have a not() function but we lack or() and and() ?

(Personal opinion)

Functionality of "or" and "and" operators are already available as (i.e, with relevant operators):
x or y
x and y

I don't think we should provide a superfluous definitions for these with additional or() and and() functions.

btw, I'm happy with an existing not(x) :)
Comment 17 Michael Kay 2010-07-19 14:07:19 UTC
Concerning points 1-5 in the original bug report, the WG accepted the editor's assurance that he had fixed the spec along the lines suggested.

Concerning points 6-7 in the original bug report, the WG decided to specify the functions such as sin(), cos(), sqrt() etc by reference to the IEEE 708 specification, allowing latitude (for example, different rounding modes, different overflow behavior) where the IEEE specification allows latitude. The concept of returning the value "either side of" the mathematical result will therefore disappear from the spec.

Concerning comment #6 raised by myself, the WG decided to add the following functions: exp, exp10, log, log10, pow, powr, and atan2, defined in terms of the IEEE 708 specification. This decision was supported by informal discussion of use cases. There was no specific direction on how the functions should be named. 

* exp, log, log10, and atan2 exist under those names in both Java and .NET as well as in IEEE, so there seems no reason to use a different name, despite the fact that ln is possibly more familiar to mathematicians. 

* powr is available under the name pow in Java and .NET. I propose that we define the two IEEE functions as overloads of a single pow() function in F+O, using the semantics of pow when the second argument is an integer, and powr otherwise. (IEEE defines pow only for floating point. We could argue that given our type system, pow(10, 6) should return an integer. However, on some implementations integer arithmetic will quickly overflow. A possible solution is to define pow(double, double)->double, and ipow(int, int)->int where the second argument is required to be non-negative.

* exp10 does not appear to be directly supported in Java or .NET. The function call exp10(x) is equivalent to pow(10, x) except possibly in the exact type or precision of the result. In our environment I suspect most use cases for exp10 are better handled with decimal arithmetic rather than binary floating point, so I propose to leave this one out.

It was noted in discussion that we don't make the constant e available directly, but that the mathematically literate will be comfortable with writing exp(1).

As no positive resolution of this bug is contained in the draft minutes of the meeting, I'm leaving the status as NEW until such a resolution is confirmed.
Comment 18 Michael Kay 2010-09-21 16:35:54 UTC
On 21 Sept 2010 the WG confirmed that it wishes to accept the proposals in comment #17 as fixing this bug.