Feature #21527
openProposal: Math.log1p and Math.expm1
Description
Let's add Math.log1p
and Math.expm1
.
-
Math.log1p(x)
: ComputesMath.log(x + 1)
-
Math.expm1(x)
: ComputesMath.exp(x) - 1
These methods are often more accurate than the straightforward computation, especially when x
is close to zero.
# The current approach loses precision
p Math.log(1 + 1.0e-16) #=> 0.0
p Math.exp(1.0e-16) - 1 #=> 0.0
# The proposed methods return the accurate result
p Math.log1p(1.0e-16) #=> 1.0e-16
p Math.expm1(1.0e-16) #=> 1.0e-16
Note that they are very standard; the C99 even defines log1p()
and expm1()
. Other major programming languages (Python, JavaScript, Java, Go, Rust, etc.) also provide them.
Updated by Eregon (Benoit Daloze) 3 days ago
ยท Edited
I find these method names pretty cryptic, typical of the libc (libm here actually) I guess.
How about:
-
Math.log_of_one_plus(x)
/Math.log_one_plus(x)
-
Math.exp_of_minus_one(x)
/Math.exp_minus_one(x)
A bit verbose, but I think these methods are (I think) very rarely needed, so that seems acceptable.
Actually, given they are probably so rarely needed, why not just using FFI or Fiddle to call them for the few cases that need them?
Updated by Eregon (Benoit Daloze) 3 days ago
require 'fiddle'
log1p = Fiddle::Function.new(Fiddle.dlopen(nil)["log1p"], [Fiddle::TYPE_DOUBLE], Fiddle::TYPE_DOUBLE)
p log1p.call(1.0e-16) # => 1.0e-16
Updated by mame (Yusuke Endoh) 2 days ago
I investigated how other languages name the log1p
function:
-
log1p
: Python, Java, JavaScript, Go, PHP, .NET, R, MATLAB, Kotlin, Swift, Julia, Haskell, OCaml, Crystal -
ln_1p
: Rust - Not natively provided (unless I missed it): Perl, Lua, Dart, Erlang
As you can see, most languages provide this function under the name log1p
. Rust uses a slightly different name, which is more cryptic to me :-)
In fact, I first noticed Ruby didn't have "Math.log1p" when I tried to write code sample code written in Python into Ruby.
Given this, I believe it's best to follow the common naming convention here.
POSIX, libc, and many languages provide them. Needing FFI to access it in Ruby isn't ideal.
https://docs.python.org/3.13/library/math.html#math.log1p
https://numpy.org/devdocs/reference/generated/numpy.log1p.html
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log1p
https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/Math.html#log1p(double)
https://pkg.go.dev/math#Log1p
https://www.php.net/manual/en/function.log1p.php
https://learn.microsoft.com/en-us/dotnet/api/java.lang.math.log1p?view=net-android-34.0
https://www.mathworks.com/help/matlab/ref/double.log1p.html
https://doc.rust-lang.org/std/primitive.f64.html#method.ln_1p
https://developer.android.com/reference/kotlin/java/lang/Math#log1p
https://developer.apple.com/documentation/simd/log1p
https://docs.julialang.org/en/v1/base/math/#Base.log1p
https://hackage.haskell.org/package/base-4.21.0.0/docs/Numeric.html#v:log1p
https://ocaml.org/manual/4.02/libref/Pervasives.html#VALlog1p
https://crystal-lang.org/api/1.17.1/Math.html#log1p%28value%29-instance-method