Convert Numeric to BinaryFloatingPoint

I was surprised that there's no initializer for this, the caller would know the input and output types and therefore should understand the possible loss of precision (and that possibility would be documented). Is there a reason this kind of initializer is not in the Standard Library?

func foo<T: BinaryFloatingPoint>(n: some Numeric) -> T {
    T(n)
}

Numeric doesn’t really offer much to go off of; there’s no way to retrieve the value for a custom implementation of the protocol. This isn’t unique to floating-point types: Even Int has separate initializers for BinaryInteger and BinaryFloatingPoint.

1 Like

It would be very difficult to write such an initializer without further constraints. Numeric is a very general protocol--it models pretty much any ring with an identity and a notion of magnitude. Such a structure is guaranteed to hold something that looks like a subset of the integers, but can contain pretty arbitrary other "stuff" to, and figuring out how to convert that to a binary floating point type is non-obvious. What floating-point number should result from the matrix ((0, 1), (-1, 0))? From the Hurwitz quaternion 0 + 3i - 2j + k?

Beyond being underspecified (and partially as a consequence), it's almost impossible to implement such an initializer efficiently, because you can't say much about the value being converted or its representation.

What are you trying to do? It's likely that you're trying to do something that can naturally be more constrained and make this into a more tractable problem.

3 Likes

As a side note, I'm not sure that a matrix can be Numeric, as Numeric refines ExpressibleByIntegerLiteral. But even if the goal here is "extract the integer literal which produced that value", that's not always possible (as you demonstrate with the quaternion), so you're better off using BinaryInteger as the type constraint.

"Matrices" in general cannot, but square matrices of a specific size absolutely can; the ring of square matrices of any fixed dimension contains a copy of the integers (given by n -> n𝐈).

3 Likes

Thanks for clarifying, makes sense that it was meant to support a more arbitrary set of types.

The intent was to take the mean of set of Numerics which would output BinaryFloatingPoint, under the assumption that the only types conforming to Numeric would be the ones specified here Numeric | Apple Developer Documentation .

Ah, in that case you will definitely want to have a somewhat stronger constraint than just Numeric.

1 Like

To be pedantic, does it actually make sense to conform rings with zero divisors or non-commutative multiplication to Numeric though? What kind of algorithm can you write that’s generic over all such rings? (However, complex numbers are a reasonable example of something Numeric that doesn’t map into the floating point numbers in any canonical way)

This is true, but since you require your ring to have a magnitude, which presumably must satisfy some identities to allow code to be generic over it, how would you define the magnitude of a matrix so that it’s “compatible” with the integer absolute value, like |n| = |n*I|?

I’m mostly just trolling at this point ;-)

An example of a rather generic ring construction that you can write algorithms over (but doesn’t belong in a standard library) is a notion of an “exact” number (so no floats) that has Euclidean division returning a quotient and remainder, and a greatest common divisor operation. This rules out non-commutative rings, zero divisors and non-unique prime factorization, but you can still construct fractions, polynomials, rational functions, power series and various other things on top of that, and they in turn will implement the same protocol.

For matrices, any of the operator norms would satisfy this requirement, as would the elementwise infinity norm. To be clear, I think we maybe shouldn't have required magnitude for Numeric, but it's definitely not a problem for square matrices.

1 Like