Why does Swift not support rational numbers?

There is no support for rational numbers. What is the reason for this?

It would be really nice to have a built-in type for them. Then, it would be possible to write code for very accurate linear transformations, without worrying about the floating point errors when computing inverse transformations.

For example:

struct RationalNumber {
    typealias NaturalNumber = Uint

    let numerator  : NaturalNumber
    let denominator: NaturalNumber
    let negative   : Bool

    init (numerator: NaturalNumber, negative: Bool = false) {
        self.init (numerator: numerator, denominator: 1, negative: negative)
    }

    init (numerator: NaturalNumber, denominator: NaturalNumber, negative: Bool = false) {
       self.numerator   = numerator
       self.denominator = denominator
       self.negative    = negative
    }

   func add      (_ u: Self) -> Self
   func subtract (_ u: Self) -> Self
   func multiply (_ u: Self) -> Self
   func divide   (_ u: Self) -> Self
}

There are third-party libraries out there, but it would be nice to have a built-in type for this.

What do you think? :slight_smile:

It has come up a few times before.

If the use-cases really are so limited, it is probably more appropriate to encourage people to use a package (perhaps swift-numerics, or one of the other third-party libraries you mention) rather than adding these to the standard library.

7 Likes

i am not sure if the first two observations support the conclusion here. in a lot of financial applications, there are formulas that must be applied in exact rational form, but the operation depth is fixed such that the implementation does not “overflow immediately”, although the number of bits needed to represent the result may be very large, larger than can fit into a Int64.

in the end, we must always round the result such that customers are not transacting in increments of dust, but it is incorrect to say it is “always strictly better to use floating-point”. sometimes the rounding is decimal-based, sometimes it is contract-defined, and sometimes it is designed simply to rob people of their dust (way more common than you think, because it’s almost impossible to notice, and usually dwarfed by explicit fees anyway.) but it is very, very rarely performed using floating-point arithmetic.

there are a lot of reasons for this, a few of which i can think off the top of my head:

  • business-type people tend to have a hard time understanding floating point,
  • contracts are usually defined using ratios, and executing them with floating point opens up a small but non-trivial possibility of major headaches down the road. (but i’ve personally never witnessed anyone get sued over it, because the amounts in dispute are usually quite small. and truth be told, there are a lot of shady things being done to dust on purpose anyway.)
  • floating point transactions are really hard to audit, because of scale invariance. this doesn’t always involve wrongdoing, a lot of times people just make small mistakes implementing things, and having a large error tolerance to account for floating point error makes it harder to catch these kinds of bugs.

but i think the first of those reasons is probably by far the most important.

the most commonly-occurring denominators for these kinds of rational parameters are multiples of 10, so this tends to get boxed into a “decimal problem”. but i’ve also seen denominators like 24, 60, etc. compounded interest is weird.

1 Like

I believe OP actually meant "fixed" point math hence the usage of 32/64 bit UInt for both numerator and denominator in the leading example:

typealias NaturalNumber = Uint
let numerator  : NaturalNumber
let denominator: NaturalNumber

I would welcome first class support for Fixed numbers. Don't do finance, but hate when I see floating point gotchas in graphics, e.g. rect math acting weirdly because of this:

let a = 100.33333333333333333333333
let b = a + 100
let c = b - 100
assert(a == c) // Assertion failed
1 Like

It also doesn't help that Decimal still has known UX and parsing issues. Namely, only ever roundtrip through String, do not parse directly from things like JSON numbers. Interactions with NSNumber and NSDecimalNumber can be tricky.

1 Like