Last active
October 10, 2019 17:30
-
-
Save kitschpatrol/5ce41e8505639eb5e9ce31a14815b43b to your computer and use it in GitHub Desktop.
Turn a decimal number into a fractional approximation rounded to a maximum denominator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import UIKit | |
// Turn a decimal number into a fractional approximation rounded to a maximum denominator | |
// https://github.com/jadengeller/fractional? | |
// https://stackoverflow.com/questions/35895154/decimal-to-fraction-conversion-in-swift#35895607 | |
typealias Rational = (whole: Int, numerator: Int, denominator: Int) | |
func decimalToRational(of value: Double, withDenominator denominator: Int) -> Rational { | |
let wholePortion = floor(value) | |
let decimalPortion = value - wholePortion | |
let numerator: Int = Int(round(decimalPortion * Double(denominator))) | |
guard numerator != 0 else { return (Int(wholePortion), 0, 0) } | |
guard numerator != denominator else { return (Int(wholePortion + 1), 0, 0) } | |
return (Int(wholePortion), numerator, denominator) | |
} | |
func gcd(_ m: Int, _ n: Int) -> Int { | |
let r: Int = m % n | |
if r != 0 { | |
return gcd(n, r) | |
} else { | |
return n | |
} | |
} | |
func reduce(rational: Rational) -> Rational { | |
guard rational.denominator != 0 else { return rational } | |
var divisor = gcd(rational.numerator, rational.denominator) | |
if divisor < 0 { divisor *= -1 } | |
guard divisor != 0 else { return (rational.whole, rational.numerator, 0) } | |
return (rational.whole, rational.numerator / divisor, rational.denominator / divisor) | |
} | |
// Quick Test | |
for index in 1 ... 150 { | |
let measurementCm = Double(index) | |
let measurementInchDecimal = measurementCm / 2.54 | |
let measurementInch = reduce(rational: decimalToRational(of: measurementInchDecimal, withDenominator: 8)) | |
let measurementInchDecimalTruncated = String(format: "%.3f", measurementInchDecimal) | |
print("\(measurementCm) cm = \(measurementInchDecimalTruncated) in ≈ \(measurementInch.whole) \(measurementInch.numerator)/\(measurementInch.denominator) in") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment