apple / swift-numerics

Advanced mathematical types and functions for Swift

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Possible improvement in 'pow' Elementary Function when operands are zero

albixp opened this issue · comments

commented

Some elementary functions might require to catch when one or both operands are zero to report the correct result. One of those is pow and of course this is applicable for both implementation in Complex and Real Module:

Issue
Result of Complex.pow(0, 0) is .infinite instead 1.
Package
swift-numerics 1.0.2
Modules
Complex Module / Elementary Functions
Real Module / Elementary Functions

**Example of Complex Module 'pow' possible fix **

extension Complex: ElementaryFunctions {
  ...
   // MARK_ - pow-like function catching both inputs equal to zero
   @inlinable
   public static func pow(_ z: Complex, _ w: Complex) -> Complex {
       if z.isZero && w.isZero { return .one} // Proposed improvement
       if z.isZero { return .zero } // Proposed improvement
       return exp(w * log(z))
   }

 @inlinable
 public static func pow(_ z: Complex, _ n: Int) -> Complex {
   if z.isZero && n == 0 { return .one} // Proposed improvement
   if z.isZero { return .zero }
   // TODO: this implementation is not quite correct, because n may be
   // rounded in conversion to RealType. This only effects very extreme
   // cases, so we'll leave it alone for now.
   //
   // Note that this does not have the same problems that a similar
   // implementation for a real type would have, because there's no
   // parity/sign interaction in the complex plane.
   return exp(log(z).multiplied(by: RealType(n)))
 }

}

You’ve identified a real issue, and I think we ought to check the sign of the real part of the exponent as well:

if z.isZero {
  if w.isZero { return .one }
  if w.real > 0 { return .zero }
  return .infinity
}

pow(x, y) is defined by exp(y log(x)), which has an essential singularity at (0, 0):

  /// exp(y * log(x)) computed with additional internal precision.
  ///
  /// See also `sqrt()` and `root()`.
  ///
  static func pow(_ x: Self, _ y: Self) -> Self

The suggested change would be a bug, except for the Complex.pow(_: Self, _: Int) case.

pow(x, y) is defined by exp(y log(x))

Does that mean pow(.zero, w) is always infinite for any w: Complex?

And if so, is that actually what we want?

Yes, that's correct. pow(_:Self,_:Self) binds the IEEE 754 powr function for real types, and extends its definition for complex types in the obvious fashion (which means that we should fix this case for real types as well).

pow(_:Self,_:Int) binds the IEEE 754 pown function, with 0, 1, +infinity results.