Project

General

Profile

Bug #2189

Math.atanh(1) & Math.atanh(-1) should not raise an error

Added by marcandre (Marc-Andre Lafortune) over 9 years ago. Updated about 8 years ago.

Status:
Closed
Priority:
Normal
Target version:
ruby -v:
ruby 1.9.2dev (2009-10-09 trunk 25274) [x86_64-darwin10.0.0]
Backport:
[ruby-core:26028]

Description

=begin
Regression caught by RubySpec.

$ rubydev -v -e 'p Math.atanh(1)'
ruby 1.9.2dev (2009-10-09 trunk 25274) [x86_64-darwin10.0.0]
-e:1:in atanh': Numerical argument out of domain - atanh (Errno::EDOM)
from -e:1:in
'
$ ruby187 -v -e 'p Math.atanh(1)'
ruby 1.8.7 (2008-08-11 patchlevel 72) [universal-darwin10.0]
Infinity

Fixed in r25279
=end


Files

pole.diff (3.79 KB) pole.diff marcandre (Marc-Andre Lafortune), 11/01/2009 12:54 PM

Related issues

Related to Ruby trunk - Bug #368: 境界における Math.atanh 等の動作Closed07/28/2008Actions
Related to Ruby trunk - Bug #2756: Issues with Math and Complex behavior on 1.9RejectedActions

History

#1

Updated by naruse (Yui NARUSE) over 9 years ago

  • Status changed from Closed to Assigned

=begin
This is from [ruby-dev:35669] (sorry for forgot describe this in r18252's log.
We discussed in it about differences of the result of Math.atanh(1).

In NetBSD 4, atanh(1) is NaN.

RETURN VALUES
If |x|>=1, atanh(x) and atanhf(x) return +inf, -inf or NaN, and sets the
global variable errno to EDOM.

But in FreeBSD 7, atanh(1) is infinity.

RETURN VALUES
The atanh() and the atanhf() functions return the inverse hyperbolic tan-
gent of x if successful. If the argument has absolute value 1, a divide-
by-zero exception is raised and an infinity is returned. If |x| > 1, an
invalid exception is raised and an NaN is returned.

In C99, described only atanh may cause ERANGE.

So we follow SUSv3, which defines atanh(x) as "If x is ±1, a pole error shall occur, (...)"
And make atanh raise Errno::EDOM or Errno::ERANGE.

So, that behavior is intended.
=end

#2

Updated by marcandre (Marc-Andre Lafortune) over 9 years ago

  • Status changed from Assigned to Open

=begin
I should have ran make test-all, I'm sorry. I also should have realized that the correct solution should have been more careful about platform independence. I'll be more careful in the future. I will also have to find out why I currently get 6 failures and 3 errors running test-all...


As to the question if atanh should raise an error or return +/- infinity, I'd like to point out:

a) The SUSv3 sentence given above continues and states "If x is ±1, a pole error shall occur, and atanh() shall return the value of the macro HUGE_VAL with the same sign as the correct value of the function."

Since it is not possible to both return a value and raise an error in Ruby, a choice must be made even if one wants to follow SUSv3.

Also, isn't a pole error different from a range error or a domain error in that document?

b) In math, atanh(tanh(x)) == x. Since tanh(x=1.0/0) doesn't raise an error, I believe atanh should not raise an error and return x.

c) atanh(±1) returns ±infinity in the following programs:

  • Mathematica
  • Matlab
  • Python
  • C++ boost library

I therefore suggest to make an explicit check for ±1 and return ±Infinity in those cases, so to have a result that is:

  • platform independent (1.8.x's result is currently platform dependent)
  • useful
  • mathematically valid

Thanks
=end

#3

Updated by marcandre (Marc-Andre Lafortune) over 9 years ago

=begin
I should have mentioned that JRuby also returns ±Infinity for atanh(±1).

I would also recommend that whatever choice is made, Ruby 1.8.x should behave the same way as Ruby 1.9 (at least for x >= 8).
=end

#4

Updated by hasari (Hiro Asari) over 9 years ago

=begin
I "fixed" it in JRuby, actually (http://github.com/jruby/jruby/commit/493da7a26d3df0d6cb784356ad8f3fca65ae1cce).

If we deem that Math.atanh(1)=Infinity, of course, JRuby will have to revert that change. This change might go into 1.4, so it's best if we could decide soon. Personally, I vote for Math.atanh(1)=Infinity.
=end

#5

Updated by yugui (Yuki Sonoda) over 9 years ago

=begin
Hi,

I reverted r25279. You had to discuss at ruby-core about how Math
module should behave before changing it.

2009/10/10 Marc-Andre Lafortune redmine@ruby-lang.org:

Regression caught by RubySpec.

$ rubydev -v -e 'p Math.atanh(1)'
ruby 1.9.2dev (2009-10-09 trunk 25274) [x86_64-darwin10.0.0]
-e:1:in atanh': Numerical argument out of domain - atanh (Errno::EDOM)
       from -e:1:in
'
$ ruby187 -v -e 'p Math.atanh(1)'
ruby 1.8.7 (2008-08-11 patchlevel 72) [universal-darwin10.0]
Infinity

As akr mentioned, your commit broke test-all. You should make test-all
before commit, particularly when you changed some feature.
If there is difference between test-all and rubyspec, maybe test-all is right.

Ruby 1.9.2's behavior around Numeric, Math, CMath, Mathn and
BigDecimal has been discussed in order to bring more consistency on
them.
So difference between 1.8 and 1.9 around them does not always mean
1.9's regression. They are under discussion (at ruby-dev).

Then, OK everyone. Which is correct? domain error or infinity?

-- Yuki Sonoda (Yugui)

=end

#6

Updated by marcandre (Marc-Andre Lafortune) over 9 years ago

=begin
Hi,

I looked at SUSv3 a bit more carefully. I'll refer interested parties to http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap04.html#tag_04_18_02

Yui NARUSE wrote:

So we follow SUSv3, which defines atanh(x) as "If x is ±1, a pole error shall occur, (...)"
And make atanh raise Errno::EDOM or Errno::ERANGE.

This appears to be inexact, as Ruby does not follow SUSv3 faithfully.

First, SUSv3 clearly distinguishes between what it calls "domain errors", "range errors" and "pole errors". Pole errors are distinct errors and are neither a "domain error" nor a "range error". Returning "Errno::EDOM: Numerical argument out of domain" in a "pole error" situation is in contradiction of SUSv3.

Second, Ruby is not even consistent with SUSv3 about "pole errors". SUSv3 states that pole errors occurs in the following cases:
Math.log(0.0)
Math.atanh(1.0)
Math.gamma(0.0)
0.0**(-n)
besel functions (no ruby equivalent)

Ruby raises the (wrong) exception in the first two cases only, but treatment should be the same according to SUSv3.

Ok, so what do we do?

SUSv3 provides a mechanism to handle "domain errors", "range errors" and "pole errors", and this mechanism is independent per type of error. Ruby doesn't provide a way to handle these cases.

Either Ruby makes valid choices on how to handle these types of errors, or else it should implement a new mechanism to handle these situations.

A mechanism could be implemented with a thread local option, say Math.raise_on_pole = true/false, or even hook, say for example:
Math.on_pole=lambda{|object, operator, arguments, result| ... }

I am not convinced this is really necessary, since valid choices can be made.

Range errors and Domain errors occur in situations where no mathematically valid result can be given. The only reasonable options are to either return NaN or raise an error. So raising an error is quite reasonable, in particular since NaN is very rarely useful.

Quote from SUSv3: A "pole error" occurs if the mathematical result of the function is an exact infinity (for example, log(0.0)).
Since it is possible to return this "exact mathematical result", I strongly believe that Ruby should return the right Infinity.

I'm attaching a patch that brings all pole errors in line and returning the right values on all platforms.

Is there any opposition to this patch?

=end

#7

Updated by mame (Yusuke Endoh) over 9 years ago

=begin
Hi,

2009/11/1 Marc-Andre Lafortune redmine@ruby-lang.org:

A mechanism could be implemented with a thread local option, say Math.raise_on_pole = true/false, or even hook, say for example:
Math.on_pole=lambda{|object, operator, arguments, result| ... }

How about Math.log_with_pole_check()?

Quote from SUSv3: A "pole error" occurs if the mathematical result of the function is an exact infinity (for example, log(0.0)).
Since it is possible to return this "exact mathematical result", I strongly believe that Ruby should return the right Infinity.

+1

Just my 2 cents,

--
Yusuke ENDOH mame@tsg.ne.jp

=end

#8

Updated by marcandre (Marc-Andre Lafortune) over 9 years ago

=begin
Hi,

On Sun, Nov 1, 2009 at 12:13 AM, Yusuke ENDOH mame@tsg.ne.jp wrote:

How about Math.log_with_pole_check()?

Since there are more functions than #log which can return +/- infinity, it would be better to have one way to handle this for all methods.

Quote from SUSv3: A "pole error" occurs if the mathematical result of the function is an exact infinity (for example, log(0.0)).
Since it is possible to return this "exact mathematical result", I strongly believe that Ruby should return the right Infinity.

+1

Thanks. If there are no further comments on this, I'll commit this in a few days and will leave it to anyone who feels there should be a special handler for pole checking to open a feature request.

Marc-André

=end

#9

Updated by matz (Yukihiro Matsumoto) over 9 years ago

=begin
Hi,

In message "Re: [ruby-core:26465] [Bug #2189] Math.atanh(1) & Math.atanh(-1) should not raise an error"
on Sun, 1 Nov 2009 12:54:45 +0900, Marc-Andre Lafortune redmine@ruby-lang.org writes:

|> So we follow SUSv3, which defines atanh(x) as "If x is ±1, a pole error shall occur, (...)"
|> And make atanh raise Errno::EDOM or Errno::ERANGE.
|
|This appears to be inexact, as Ruby does not follow SUSv3 faithfully.
|
|First, SUSv3 clearly distinguishes between what it calls "domain errors", "range errors" and "pole errors". Pole errors are distinct errors and are neither a "domain error" nor a "range error". Returning "Errno::EDOM: Numerical argument out of domain" in a "pole error" situation is in contradiction of SUSv3.

Forgive my ignorance but what is a pole error in practical UNIX
programing sense. On my Linux box, atanh document only describes EDOM
to be raised. Does that mean, atanh on Linux not following SUSv3?
In that case, when underlying Linux does not, should we?

                        matz.

=end

#10

Updated by runpaint (Run Paint Run Run) over 9 years ago

=begin
For completeness, I'll note that IEE Std. 754 (2008) recommends (pp. 42-43) that atanh(x) signals a "divideByZero" exception when x == 1; and a "invalid operation" exception when x > 1. Both forms raise the "underflow" flag. Later (pp. 43-45) it is stated that "atanh(±1) is ±∞ and signals the divideByZero exception."

=end

#11

Updated by marcandre (Marc-Andre Lafortune) over 9 years ago

=begin
Hi,

2009/11/9 Yukihiro Matsumoto matz@ruby-lang.org:

Forgive my ignorance but what is a pole error in practical UNIX
programing sense. On my Linux box, atanh document only describes EDOM
to be raised. Does that mean, atanh on Linux not following SUSv3?

Apparently not, at least according to the reference I found online:
http://www.opengroup.org/onlinepubs/009695399/functions/atanh.html
http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap04.html#tag_04_18_02

In that case, when underlying Linux does not, should we?

This it the most pertinent question.

Should Ruby tend towards being an interface to Unix, or tend towards being a platform.

By being a platform, I mean that a rubyist can expect to be shielded as much as possible from the differences due to its environment and can expect the same result no matter which OS Ruby runs on, or even which implementation of Ruby it is running on.

I believe it is best to keep the platform dependent methods to a strict minimum (when it is unavoidable or when it is useful) and make the best "universal" choices for the rest.

Maybe because of my background in mathematics, but I hope it is decided that Ruby can deal with infinity. Maybe there can be a mechanism to trap Infinities (Math.log(0) and 1.0/0 as well), but I hope that by default these return infinities.

I noticed yesterday that the BigDecimal library has such a mechanism. I don't personally like it much, but I thought it should be mentioned.

=end

#12

Updated by marcandre (Marc-Andre Lafortune) over 9 years ago

  • Status changed from Open to Closed

=begin
Resolved with r26785.
=end

Also available in: Atom PDF