Search code examples
swiftgenericsdecimal

Swift generic Decimal divide operator missing


I was trying to come up with some code using generics to switch between Double and Decimal and setup this simple example using all the "Conforms To" relationships in Decimal which is a subset of the relationships listed in Double:

func generic_calc_1<T: Comparable & CustomStringConvertible & Decodable & Encodable & ExpressibleByFloatLiteral & ExpressibleByIntegerLiteral & Hashable & Plottable & Sendable & SignedNumeric & Strideable>(a: T, b: T) -> T
{
    return (a + b) * (b - a)
}

print("Calc1: Double:", generic_calc_1(a: Double(2), b: Double(3)))
print("Calc1: Decimal:", generic_calc_1(a: Decimal(2), b: Decimal(3)))

That is well and good but when I use the divide operator I get an error:

func generic_calc_2<T: Comparable & CustomStringConvertible & Decodable & Encodable & ExpressibleByFloatLiteral & ExpressibleByIntegerLiteral & Hashable & Plottable & Sendable & SignedNumeric & Strideable>(a: T, b: T) -> T
{
    return a / b //error: binary operator '/' cannot be applied to two 'T' operands
}

This seems really strange and feels like a bug. But I suppose there could be reasons for this? I can't imagine what. I suppose there are ways to work around this but it seems this should work out of the box. Am I missing something here?


Solution

  • The underlying issue is that Decimal does not conform to FloatingPoint. This is because while, in theory, FloatingPoint does not imply BinaryFloatingPoint, in practice it does. And Decimal does not (and cannot) conform to BinaryFloatingPoint.

    As you say, Decimal has a subset of the protocols of Double, and none of those include / (which comes from FloatingPoint).

    So trying to bridge Decimal into other types is always awkward and requires you to choose how to handle it.

    It is recognized as a problem, and there is work going on to address it.