[ruby-core:122898] [Ruby Feature#21527] Proposal: Math.log1p and Math.expm1

Issue #21527 has been reported by mame (Yusuke Endoh). ---------------------------------------- Feature #21527: Proposal: Math.log1p and Math.expm1 https://bugs.ruby-lang.org/issues/21527 * Author: mame (Yusuke Endoh) * Status: Open ---------------------------------------- Let's add `Math.log1p` and `Math.expm1`. * `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 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. PR: https://github.com/ruby/ruby/pull/14087 -- https://bugs.ruby-lang.org/

Issue #21527 has been updated by Eregon (Benoit Daloze). I find these method names pretty cryptic, typical of the libc 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? ---------------------------------------- Feature #21527: Proposal: Math.log1p and Math.expm1 https://bugs.ruby-lang.org/issues/21527#change-114225 * Author: mame (Yusuke Endoh) * Status: Open ---------------------------------------- Let's add `Math.log1p` and `Math.expm1`. * `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 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. PR: https://github.com/ruby/ruby/pull/14087 -- https://bugs.ruby-lang.org/

Issue #21527 has been updated by Eregon (Benoit Daloze). ```ruby 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 ``` ---------------------------------------- Feature #21527: Proposal: Math.log1p and Math.expm1 https://bugs.ruby-lang.org/issues/21527#change-114226 * Author: mame (Yusuke Endoh) * Status: Open ---------------------------------------- Let's add `Math.log1p` and `Math.expm1`. * `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 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. PR: https://github.com/ruby/ruby/pull/14087 -- https://bugs.ruby-lang.org/

Issue #21527 has been updated by mame (Yusuke Endoh). 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_Obj... https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/lang/Math....) 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-a... 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-meth... ---------------------------------------- Feature #21527: Proposal: Math.log1p and Math.expm1 https://bugs.ruby-lang.org/issues/21527#change-114230 * Author: mame (Yusuke Endoh) * Status: Open ---------------------------------------- Let's add `Math.log1p` and `Math.expm1`. * `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 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. PR: https://github.com/ruby/ruby/pull/14087 -- https://bugs.ruby-lang.org/

Issue #21527 has been updated by matz (Yukihiro Matsumoto). Accepted. Matz. ---------------------------------------- Feature #21527: Proposal: Math.log1p and Math.expm1 https://bugs.ruby-lang.org/issues/21527#change-114305 * Author: mame (Yusuke Endoh) * Status: Open ---------------------------------------- Let's add `Math.log1p` and `Math.expm1`. * `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 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. PR: https://github.com/ruby/ruby/pull/14087 -- https://bugs.ruby-lang.org/
participants (3)
-
Eregon (Benoit Daloze)
-
mame (Yusuke Endoh)
-
matz (Yukihiro Matsumoto)