Skip to content

Commit f2b1017

Browse files
committed
Add Math.log1p and Math.expm1
This commit adds two new methods to the `Math` module: * `Math.log1p(x)`: Computes `Math.log(x + 1)` * `Math.expm1(x)`: Computes `Math.exp(x) - 1` These methods are often more accurate than the straightforward computation, especially when `x` is close to zero. The corresponding functions, `log1p` and `expm1`, are defined in the C99 standard math library. [Feature #21527]
1 parent feb8331 commit f2b1017

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed

math.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,34 @@ math_exp(VALUE unused_obj, VALUE x)
457457
return DBL2NUM(exp(Get_Double(x)));
458458
}
459459

460+
/*
461+
* call-seq:
462+
* Math.expm1(x) -> float
463+
*
464+
* Returns "exp(x) - 1", +e+ raised to the +x+ power, minus 1.
465+
*
466+
*
467+
* - Domain: <tt>[-INFINITY, INFINITY]</tt>.
468+
* - Range: <tt>[-1.0, INFINITY]</tt>.
469+
*
470+
* Examples:
471+
*
472+
* expm1(-INFINITY) # => 0.0
473+
* expm1(-1.0) # => -0.6321205588285577 # 1.0/E - 1
474+
* expm1(0.0) # => 0.0
475+
* expm1(0.5) # => 0.6487212707001282 # sqrt(E) - 1
476+
* expm1(1.0) # => 1.718281828459045 # E - 1
477+
* expm1(2.0) # => 6.38905609893065 # E**2 - 1
478+
* expm1(INFINITY) # => Infinity
479+
*
480+
*/
481+
482+
static VALUE
483+
math_expm1(VALUE unused_obj, VALUE x)
484+
{
485+
return DBL2NUM(expm1(Get_Double(x)));
486+
}
487+
460488
#if defined __CYGWIN__
461489
# include <cygwin/version.h>
462490
# if CYGWIN_VERSION_DLL_MAJOR < 1005
@@ -646,6 +674,47 @@ math_log10(VALUE unused_obj, VALUE x)
646674
return DBL2NUM(log10(d) + numbits * log10(2)); /* log10(d * 2 ** numbits) */
647675
}
648676

677+
/*
678+
* call-seq:
679+
* Math.log1p(x) -> float
680+
*
681+
* Returns "log(x + 1)", the base E {logarithm}[https://en.wikipedia.org/wiki/Logarithm] of (+x+ + 1).
682+
*
683+
* - Domain: <tt>[-1, INFINITY]</tt>.
684+
* - Range: <tt>[-INFINITY, INFINITY]</tt>.
685+
*
686+
* Examples:
687+
*
688+
* log1p(-1.0) # => -Infinity
689+
* log1p(0.0) # => 0.0
690+
* log1p(E - 1) # => 1.0
691+
* log1p(INFINITY) # => Infinity
692+
*
693+
*/
694+
695+
static VALUE
696+
math_log1p(VALUE unused_obj, VALUE x)
697+
{
698+
size_t numbits;
699+
double d = get_double_rshift(x, &numbits);
700+
701+
if (numbits != 0) {
702+
x = rb_big_plus(x, INT2FIX(1));
703+
d = math_log_split(x, &numbits);
704+
/* check for pole error */
705+
if (d == 0.0) return DBL2NUM(-HUGE_VAL);
706+
d = log(d);
707+
d += numbits * M_LN2;
708+
return DBL2NUM(d);
709+
}
710+
711+
domain_check_min(d, -1.0, "log1p");
712+
/* check for pole error */
713+
if (d == -1.0) return DBL2NUM(-HUGE_VAL);
714+
715+
return DBL2NUM(log1p(d)); /* log10(d * 2 ** numbits) */
716+
}
717+
649718
static VALUE rb_math_sqrt(VALUE x);
650719

651720
/*
@@ -1120,9 +1189,11 @@ InitVM_Math(void)
11201189
rb_define_module_function(rb_mMath, "atanh", math_atanh, 1);
11211190

11221191
rb_define_module_function(rb_mMath, "exp", math_exp, 1);
1192+
rb_define_module_function(rb_mMath, "expm1", math_expm1, 1);
11231193
rb_define_module_function(rb_mMath, "log", math_log, -1);
11241194
rb_define_module_function(rb_mMath, "log2", math_log2, 1);
11251195
rb_define_module_function(rb_mMath, "log10", math_log10, 1);
1196+
rb_define_module_function(rb_mMath, "log1p", math_log1p, 1);
11261197
rb_define_module_function(rb_mMath, "sqrt", math_sqrt, 1);
11271198
rb_define_module_function(rb_mMath, "cbrt", math_cbrt, 1);
11281199

test/ruby/test_math.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,13 @@ def test_exp
147147
check(Math::E ** 2, Math.exp(2))
148148
end
149149

150+
def test_expm1
151+
check(0, Math.expm1(0))
152+
check(Math.sqrt(Math::E) - 1, Math.expm1(0.5))
153+
check(Math::E - 1, Math.expm1(1))
154+
check(Math::E ** 2 - 1, Math.expm1(2))
155+
end
156+
150157
def test_log
151158
check(0, Math.log(1))
152159
check(1, Math.log(Math::E))
@@ -201,6 +208,19 @@ def test_log10
201208
assert_nothing_raised { assert_infinity(-Math.log10(0)) }
202209
end
203210

211+
def test_log1p
212+
check(0, Math.log1p(0))
213+
check(1, Math.log1p(Math::E - 1))
214+
check(Math.log(2.0 ** 64 + 1), Math.log1p(1 << 64))
215+
check(Math.log(2) * 1024.0, Math.log1p(2 ** 1024))
216+
assert_nothing_raised { assert_infinity(Math.log1p(1.0/0)) }
217+
assert_nothing_raised { assert_infinity(-Math.log1p(-1.0)) }
218+
assert_raise_with_message(Math::DomainError, /\blog1p\b/) { Math.log1p(-1.1) }
219+
assert_raise_with_message(Math::DomainError, /\blog1p\b/) { Math.log1p(-Float::EPSILON-1) }
220+
assert_nothing_raised { assert_nan(Math.log1p(Float::NAN)) }
221+
assert_nothing_raised { assert_infinity(-Math.log1p(-1)) }
222+
end
223+
204224
def test_sqrt
205225
check(0, Math.sqrt(0))
206226
check(1, Math.sqrt(1))

0 commit comments

Comments
 (0)