Difficulties extending BinaryFloatingPoint


(Jens Persson) #1

The code below doesn't compile since there is no exponential function (exp)
that works on all FloatingPoint or BinaryFloatingPoint types, also no
protocol seems to define the power function or the constant e, although
they do define for example: basic arithmetic operators, squareRoot() and pi.

extension BinaryFloatingPoint {
    func sigmoid() -> Self {
        return 1.0 / (1.0 + exp(-self))
    }
}

I could, but don't want to write two free funcs sigmoid(Float) -> Float and
sigmoid(Double) -> Double, because I need to use x.sigmoid() in several
places where x is of a generic type (a BinaryFloatingPoint).

More generally: I would have the same problem if I needed eg sin or cos.
Are there any particular reason why, given a generic BinaryFloatingPoint, I
cannot use sin or cos, while I can use pi and squareRoot()? It does seem a
bit arbitrary.

Any ideas on how to implement a sigmoid() that works for all
BinaryFloatingPoint types?

/Jens


(Stephen Canon) #2

Hi Jens —

BinaryFloatingPoint is very deliberately limited to the arithmetic operations required by IEEE 754. This is the minimal set of operations that a binary floating point type should provide, but it's already a really large implementation burden for someone who wants to implement their own conforming type.

I agree that there should eventually be either a refinement or orthogonal protocol[s] with the semantics “type implements [a subset of] the standard math functions”, but those shouldn’t get bolted onto BinaryFloatingPoint—implementing these functions for an arbitrary BinaryFloatingPoint type is highly non-trivial, and would make the implementation burden for a new floating point type unreasonably high. This is also out-of-scope for the current phase of Swift evolution.

In the short term for your immediate problem at hand, I’ve been doing something like:

import Darwin

public protocol Math: BinaryFloatingPoint {
  func _exp() -> Self
  func _log() -> Self
  func _sin() -> Self
  func _cos() -> Self
}

extension Double: Math {
  public func _exp() -> Double { return exp(self) }
  public func _log() -> Double { return log(self) }
  public func _sin() -> Double { return sin(self) }
  public func _cos() -> Double { return cos(self) }
}

extension Float: Math {
  public func _exp() -> Float { return exp(self) }
  public func _log() -> Float { return log(self) }
  public func _sin() -> Float { return sin(self) }
  public func _cos() -> Float { return cos(self) }
}

func exp<T: Math>(_ x: T) -> T { return x._exp() }
func log<T: Math>(_ x: T) -> T { return x._log() }
func sin<T: Math>(_ x: T) -> T { return x._sin() }
func cos<T: Math>(_ x: T) -> T { return x._cos() }

extension Math {
  func sigmoid() -> Self {
    return 1.0 / (1.0 + exp(-self))
  }
}

let x = 1.0
x.sigmoid()

Someone else might have a more clever solution.
– Steve

···

On Jan 5, 2017, at 8:22 AM, Jens Persson via swift-users <swift-users@swift.org> wrote:

The code below doesn't compile since there is no exponential function (exp) that works on all FloatingPoint or BinaryFloatingPoint types, also no protocol seems to define the power function or the constant e, although they do define for example: basic arithmetic operators, squareRoot() and pi.

extension BinaryFloatingPoint {
    func sigmoid() -> Self {
        return 1.0 / (1.0 + exp(-self))
    }
}

I could, but don't want to write two free funcs sigmoid(Float) -> Float and sigmoid(Double) -> Double, because I need to use x.sigmoid() in several places where x is of a generic type (a BinaryFloatingPoint).

More generally: I would have the same problem if I needed eg sin or cos. Are there any particular reason why, given a generic BinaryFloatingPoint, I cannot use sin or cos, while I can use pi and squareRoot()? It does seem a bit arbitrary.

Any ideas on how to implement a sigmoid() that works for all BinaryFloatingPoint types?

/Jens
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Jens Persson) #3

Great, thank you Steve!

···

On Thu, Jan 5, 2017 at 5:04 PM, Steve (Numerics) Canon <scanon@apple.com> wrote:

Hi Jens —

BinaryFloatingPoint is very deliberately limited to the arithmetic
operations required by IEEE 754. This is the minimal set of operations that
a binary floating point type should provide, but it's already a really
large implementation burden for someone who wants to implement their own
conforming type.

I agree that there should eventually be either a refinement or orthogonal
protocol[s] with the semantics “type implements [a subset of] the standard
math functions”, but those shouldn’t get bolted onto BinaryFloatingPoint—implementing
these functions for an arbitrary BinaryFloatingPoint type is highly
non-trivial, and would make the implementation burden for a new floating
point type unreasonably high. This is also out-of-scope for the current
phase of Swift evolution.

In the short term for your immediate problem at hand, I’ve been doing
something like:

import Darwin

public protocol Math: BinaryFloatingPoint {
  func _exp() -> Self
  func _log() -> Self
  func _sin() -> Self
  func _cos() -> Self
}

extension Double: Math {
  public func _exp() -> Double { return exp(self) }
  public func _log() -> Double { return log(self) }
  public func _sin() -> Double { return sin(self) }
  public func _cos() -> Double { return cos(self) }
}

extension Float: Math {
  public func _exp() -> Float { return exp(self) }
  public func _log() -> Float { return log(self) }
  public func _sin() -> Float { return sin(self) }
  public func _cos() -> Float { return cos(self) }
}

func exp<T: Math>(_ x: T) -> T { return x._exp() }
func log<T: Math>(_ x: T) -> T { return x._log() }
func sin<T: Math>(_ x: T) -> T { return x._sin() }
func cos<T: Math>(_ x: T) -> T { return x._cos() }

extension Math {
  func sigmoid() -> Self {
    return 1.0 / (1.0 + exp(-self))
  }
}

let x = 1.0
x.sigmoid()

Someone else might have a more clever solution.
– Steve

On Jan 5, 2017, at 8:22 AM, Jens Persson via swift-users < > swift-users@swift.org> wrote:

The code below doesn't compile since there is no exponential function
(exp) that works on all FloatingPoint or BinaryFloatingPoint types, also no
protocol seems to define the power function or the constant e, although
they do define for example: basic arithmetic operators, squareRoot() and pi.

extension BinaryFloatingPoint {
    func sigmoid() -> Self {
        return 1.0 / (1.0 + exp(-self))
    }
}

I could, but don't want to write two free funcs sigmoid(Float) -> Float
and sigmoid(Double) -> Double, because I need to use x.sigmoid() in several
places where x is of a generic type (a BinaryFloatingPoint).

More generally: I would have the same problem if I needed eg sin or cos.
Are there any particular reason why, given a generic BinaryFloatingPoint, I
cannot use sin or cos, while I can use pi and squareRoot()? It does seem a
bit arbitrary.

Any ideas on how to implement a sigmoid() that works for all
BinaryFloatingPoint types?

/Jens
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users