Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Fix BigMath::exp for negative immediate types, add tests for these cases #332

Closed
wants to merge 1 commit into from

3 participants

@GarthSnyder

Please note: I'm not 100% sure that this fix is the correct fix, so this patch requires careful scrutiny by someone familiar with the BigDecimal implementation. However, the test cases I added do show a clear problem with BigMath.exp of immediate types with values < 0 and may be useful to integrate regardless of whether the fix goes in.

Problem: BigMath.exp(-x, n) == BigMath.exp(x, n) whenever x is an immediate type. For example, BigMath.exp(-1, 20) is 2.718. The correct value is 0.3679 (that is, 1/2.718)

Mechanism: It appears that the VALUE x passed into BigMath_s_exp is promoted to a Real vx. At bigdecimal.c:2742, the code flips the sign bit on negative values as part of the implementation of the identity that E * -x == 1 / (E ** x). Later at line 2774, the sign is rechecked and the reciprocal of the result is returned if the original argument was negative.

However, in the calculation loop at line 2765, it's the original VALUE x that's used in the iteration instead of vx->obj. This works OK for BigDecimals since VPSetSign reaches though vx into the original x. (That is itself a separate problem, since it results in the argument being unexpectedly modified; I will submit a separate issue for that.) But immediate types still have their original (negative) values.

Fix: I suspect that it's just ToValue(vx) that's intended here instead of x.

@GarthSnyder

This issue also occurs in 1.9.3 and probably 1.8 as well.

@hsbt
Owner

@mrkn Could you review this pull request?

@mrkn
Collaborator

I'll check it tonight.

@evanphx evanphx closed this pull request from a commit
@mrkn mrkn * ext/bigdecimal/bigdecimal.c (BigMath_s_exp): Fix for the cases when
  the argument x is not a BigDecimal.
  This change is based on the patch made by Garth Snyder.
  [Fix GH-332] #332

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@41623 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
448c66c
@evanphx evanphx closed this in 448c66c
@evanphx evanphx referenced this pull request from a commit
@nagachika nagachika merge revision(s) 41623,41723: [Backport #6862] [Backport #8587]
	* ext/bigdecimal/bigdecimal.c (BigMath_s_exp): Fix for the cases when
	  the argument x is not a BigDecimal.
	  This change is based on the patch made by Garth Snyder.
	  [Fix GH-332] #332
	  This change is based on the patch made by Heesob Park and Garth Snyder.
	  [Bug #6862] [ruby-core:47145]


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_0_0@41733 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
157dd91
@evanphx evanphx referenced this pull request from a commit
@unak unak merge revision(s) 41623,41723: [Backport #8603]
	* ext/bigdecimal/bigdecimal.c (BigMath_s_exp): Fix for the cases when
	  the argument x is not a BigDecimal.
	  This change is based on the patch made by Garth Snyder.
	  [Fix GH-332] #332
	  This change is based on the patch made by Heesob Park and Garth Snyder.
	  [Bug #6862] [ruby-core:47145]


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_9_3@41909 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
24eff27
@tenderlove tenderlove referenced this pull request from a commit in tenderlove/ruby
@mrkn mrkn * ext/bigdecimal/bigdecimal.c (BigMath_s_exp): Fix for the cases when
  the argument x is not a BigDecimal.
  This change is based on the patch made by Garth Snyder.
  [Fix GH-332] ruby#332

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@41623 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
8b75ea4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 30 additions and 6 deletions.
  1. +1 −1  ext/bigdecimal/bigdecimal.c
  2. +29 −5 test/bigdecimal/test_bigdecimal.rb
View
2  ext/bigdecimal/bigdecimal.c
@@ -2762,7 +2762,7 @@ BigMath_s_exp(VALUE klass, VALUE x, VALUE vprec)
m = rmpd_double_figures();
}
- x1 = BigDecimal_mult2(x1, x, SSIZET2NUM(n));
+ x1 = BigDecimal_mult2(x1, ToValue(vx), SSIZET2NUM(n));
++i;
z = BigDecimal_mult(z, SSIZET2NUM(i));
argv[0] = z;
View
34 test/bigdecimal/test_bigdecimal.rb
@@ -1276,11 +1276,35 @@ def test_exp_with_1
end
def test_BigMath_exp
- n = 20
- assert_in_epsilon(Math.exp(n), BigMath.exp(BigDecimal("20"), n))
- assert_in_epsilon(Math.exp(40), BigMath.exp(BigDecimal("40"), n))
- assert_in_epsilon(Math.exp(-n), BigMath.exp(BigDecimal("-20"), n))
- assert_in_epsilon(Math.exp(-40), BigMath.exp(BigDecimal("-40"), n))
+ prec = 20
+ assert_in_epsilon(Math.exp(20), BigMath.exp(BigDecimal("20"), prec))
+ assert_in_epsilon(Math.exp(40), BigMath.exp(BigDecimal("40"), prec))
+ assert_in_epsilon(Math.exp(-20), BigMath.exp(BigDecimal("-20"), prec))
+ assert_in_epsilon(Math.exp(-40), BigMath.exp(BigDecimal("-40"), prec))
+ end
+
+ def test_BigMath_exp_with_float
+ prec = 20
+ assert_in_epsilon(Math.exp(20), BigMath.exp(20.0, prec))
+ assert_in_epsilon(Math.exp(40), BigMath.exp(40.0, prec))
+ assert_in_epsilon(Math.exp(-20), BigMath.exp(-20.0, prec))
+ assert_in_epsilon(Math.exp(-40), BigMath.exp(-40.0, prec))
+ end
+
+ def test_BigMath_exp_with_fixnum
+ prec = 20
+ assert_in_epsilon(Math.exp(20), BigMath.exp(20, prec))
+ assert_in_epsilon(Math.exp(40), BigMath.exp(40, prec))
+ assert_in_epsilon(Math.exp(-20), BigMath.exp(-20, prec))
+ assert_in_epsilon(Math.exp(-40), BigMath.exp(-40, prec))
+ end
+
+ def test_BigMath_exp_with_rational
+ prec = 20
+ assert_in_epsilon(Math.exp(20), BigMath.exp(Rational(40,2), prec))
+ assert_in_epsilon(Math.exp(40), BigMath.exp(Rational(80,2), prec))
+ assert_in_epsilon(Math.exp(-20), BigMath.exp(Rational(-40,2), prec))
+ assert_in_epsilon(Math.exp(-40), BigMath.exp(Rational(-80,2), prec))
end
def test_BigMath_exp_under_gc_stress
Something went wrong with that request. Please try again.