apple / swift-numerics

Advanced mathematical types and functions for Swift
Apache License 2.0
1.66k stars 140 forks source link

Feature request: GCD for floating-point #279

Open wadetregaskis opened 5 months ago

wadetregaskis commented 5 months ago

Surprisingly, I can't find any implementation of floating-point GCD for Swift, anywhere.

While there are implementations in other languages - Python has heaps - a lot of them gloss over the important detail of rounding. And for those that don't, porting them to Swift too literally may introduce mathematical errors, depending on how the source language differs from Swift in its handling of reals.

Since this library already has a good GCD implementation for integers (which seems to be a prerequisite for a floating-point variant), perhaps it'd make sense to add a version for FloatingPoint?

stephentyrone commented 3 months ago

It's not too hard to implement--the usual algorithms can be made to work--but I'm curious what you intend to use it for, since that might influence how we'd expose it.

stephentyrone commented 2 months ago

Also, what do you expect to happen when one of the values isn't an integer?

wadetregaskis commented 2 months ago

My use case is for reverting NSImage's conversion of animation timing information (expressed by NSImage as NSTimeInterval) back to its natural form (integer measures of counts over a timescale). I want to simplify the fraction back to its natural form, which means finding the greatest common divisor (given a small amount of fudge allowance, to compensate from rounding errors introduced by AppKit).

e.g. if the duration of two frames is reported by NSImage as 0.06666666667 and 0.1333333333, I want to determine that the timescale is 15 and the frame durations are 1 and 2, respectively.

The fudge allowance is crucial by the very nature of floating point, which is what makes this notably less trivial than working with integers.

stephentyrone commented 2 months ago

I think I might prefer to do that via two simpler operations:

My intuition is that these are much more explicable building blocks than a magical GCD with fudge factor, but I'm open to being convinced.

wadetregaskis commented 2 months ago

I think that makes sense. Simplifying integer fractions is a common need too.

But, that's essentially what I'm currently doing and it's maybe not perfect. As two separate steps, the degree of approximation used in the conversion to integers is essentially arbitrary. Ideally it'd be just enough - and no more - to produce a sufficiently simple fraction at the end. e.g. 1/15 instead of 9/150.

Of course, there's seemingly always some degree of arbitrariness in this, because you have to pin down what a "sufficiently simple" fraction is. But in some contexts that can have hints - e.g. for animations, as in my use case, it's much more likely that the denominator will be 15, 24, 25, 30, or 60, than any other numbers. Especially larger numbers.

stephentyrone commented 2 months ago

For this specific use, you can probably first multiply by 300 and call it a day if the result is sufficiently close to an integer, short-cutting all the rest.

wadetregaskis commented 2 months ago

That was my workaround initially, essentially (I used a million or thereabouts, not 300, but same idea). It works pretty well but isn't perfect.