Decimal+rounding.swift 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
  1. import Foundation
  2. extension Decimal {
  3. func rounded(scale: Int, roundingMode: NSDecimalNumber.RoundingMode = .plain) -> Decimal {
  4. let handler = NSDecimalNumberHandler(
  5. roundingMode: roundingMode,
  6. scale: Int16(scale),
  7. raiseOnExactness: false,
  8. raiseOnOverflow: false,
  9. raiseOnUnderflow: false,
  10. raiseOnDivideByZero: false
  11. )
  12. return NSDecimalNumber(decimal: self).rounding(accordingToBehavior: handler).decimalValue
  13. }
  14. func rounded() -> Decimal {
  15. rounded(scale: 0)
  16. }
  17. /// Implement Math.round from JS on Decimals. The JS implementation will add 0.5
  18. /// and do a floor operation, which is what we're doing here. This ends up mattering
  19. /// for values that are negative and end with .5 exactly
  20. func jsRounded(scale: Int) -> Decimal {
  21. var multiplier = (0 ..< scale).reduce(Decimal(1)) { result, _ in result * 10 }
  22. return (self * multiplier + 0.5).rounded(scale: 0, roundingMode: .down) / multiplier
  23. }
  24. // Implement Math.floor from JS on Decimals
  25. func floor() -> Decimal {
  26. rounded(scale: 0, roundingMode: .down)
  27. }
  28. func jsRounded() -> Decimal {
  29. // double rounding to help with imprecision in calculations
  30. jsRounded(scale: 6).jsRounded(scale: 0)
  31. }
  32. func clamp(lowerBound: Decimal, upperBound: Decimal) -> Decimal {
  33. if self < lowerBound {
  34. return lowerBound
  35. } else if self > upperBound {
  36. return upperBound
  37. } else {
  38. return self
  39. }
  40. }
  41. }