diff --git a/Sources/EngineKit/RETDecimal+Extensions.swift b/Sources/EngineKit/RETDecimal+Extensions.swift index fece12cd9c..72bd35bed3 100644 --- a/Sources/EngineKit/RETDecimal+Extensions.swift +++ b/Sources/EngineKit/RETDecimal+Extensions.swift @@ -87,13 +87,18 @@ extension RETDecimal { // MARK: Truncation and rounding extension RETDecimal { - /// Truncates to `decimalPlaces` decimals - public func truncated(decimalPlaces: UInt) -> RETDecimal { + /// Rounds to `decimalPlaces` decimals, in the direction of 0 + public func floor(decimalPlaces: UInt) -> RETDecimal { try! round(decimalPlaces: Int32(decimalPlaces), roundingMode: .toZero) } + /// Rounds to `decimalPlaces` decimals, in the direction away from zero + public func ceil(decimalPlaces: UInt) -> RETDecimal { + try! round(decimalPlaces: Int32(decimalPlaces), roundingMode: .awayFromZero) + } + /// Rounds to `decimalPlaces` decimals - public func rounded(decimalPlaces: UInt) -> RETDecimal { + public func rounded(decimalPlaces: UInt = 0) -> RETDecimal { try! round(decimalPlaces: Int32(decimalPlaces), roundingMode: .toNearestMidpointAwayFromZero) } } @@ -130,7 +135,8 @@ extension RETDecimal { formattedString: String, locale: Locale = .autoupdatingCurrent ) throws { - var string = formattedString + // Pad with a leading zero, to make numbers with leading decimal separator parsable + var string = "0" + formattedString // If the locale recognizes a grouping separator, we strip that from the string if let groupingSeparator = locale.groupingSeparator { string.replace(groupingSeparator, with: "") diff --git a/Sources/Features/TransactionReviewFeature/TransactionReviewGuarantees/MinimumPercentageStepper/MinimumPercentageStepper.swift b/Sources/Features/TransactionReviewFeature/TransactionReviewGuarantees/MinimumPercentageStepper/MinimumPercentageStepper.swift index c6351134c5..cc46282d59 100644 --- a/Sources/Features/TransactionReviewFeature/TransactionReviewGuarantees/MinimumPercentageStepper/MinimumPercentageStepper.swift +++ b/Sources/Features/TransactionReviewFeature/TransactionReviewGuarantees/MinimumPercentageStepper/MinimumPercentageStepper.swift @@ -13,7 +13,7 @@ public struct MinimumPercentageStepper: FeatureReducer { var string: String public init(value: RETDecimal) { - let clamped = value.rounded(decimalPlaces: 2).clamped + let clamped = value.clamped self.value = clamped self.string = clamped.formattedPlain() } @@ -34,13 +34,13 @@ public struct MinimumPercentageStepper: FeatureReducer { public func reduce(into state: inout State, viewAction: ViewAction) -> Effect { switch viewAction { case .increaseTapped: - let value = state.value.map { $0 + percentageDelta } ?? 100 + let value = state.value.map { $0.floor(decimalPlaces: 0) + percentageDelta } ?? 100 let clamped = value.clamped state.value = clamped state.string = clamped.formattedPlain() case .decreaseTapped: - let value = state.value.map { $0 - percentageDelta } ?? 0 + let value = state.value.map { $0.ceil(decimalPlaces: 0) - percentageDelta } ?? 0 let clamped = value.clamped state.value = clamped state.string = clamped.formattedPlain() @@ -59,7 +59,7 @@ public struct MinimumPercentageStepper: FeatureReducer { return .send(.delegate(.valueChanged)) } - private let percentageDelta: RETDecimal = 0.1 + private let percentageDelta: RETDecimal = 1 } extension MinimumPercentageStepper.State { diff --git a/Sources/Features/TransactionReviewFeature/TransactionReviewGuarantees/TransactionReviewGuarantees.swift b/Sources/Features/TransactionReviewFeature/TransactionReviewGuarantees/TransactionReviewGuarantees.swift index 1736414c55..2908017a82 100644 --- a/Sources/Features/TransactionReviewFeature/TransactionReviewGuarantees/TransactionReviewGuarantees.swift +++ b/Sources/Features/TransactionReviewFeature/TransactionReviewGuarantees/TransactionReviewGuarantees.swift @@ -123,7 +123,8 @@ public struct TransactionReviewGuarantee: Sendable, FeatureReducer { } let newMinimumDecimal = value * 0.01 - let newAmount = newMinimumDecimal * state.details.amount + let divisibility = state.resource.divisibility.map(UInt.init) ?? RETDecimal.maxDivisibility + let newAmount = (newMinimumDecimal * state.details.amount).rounded(decimalPlaces: divisibility) state.guarantee.amount = newAmount return .none diff --git a/Tests/EngineKitTests/DecimalTests.swift b/Tests/EngineKitTests/DecimalTests.swift index b6a999e687..2a4451c8cc 100644 --- a/Tests/EngineKitTests/DecimalTests.swift +++ b/Tests/EngineKitTests/DecimalTests.swift @@ -213,7 +213,7 @@ final class DecimalTests: TestCase { func doTest(_ decimalString: String, divisibility: UInt, expected expectedString: String, line: UInt = #line) throws { let expected = try RETDecimal(value: expectedString) let decimal = try RETDecimal(value: decimalString) - let actual = decimal.truncated(decimalPlaces: divisibility) + let actual = decimal.floor(decimalPlaces: divisibility) XCTAssertEqual(actual, expected, line: line) } @@ -265,6 +265,8 @@ final class DecimalTests: TestCase { } let spanish = Locale(identifier: "es") let us = Locale(identifier: "en_US_POSIX") + try doTest(",005", locale: spanish, expected: .init(value: "0.005")) + try doTest(".005", locale: us, expected: .init(value: "0.005")) try doTest("1,001", locale: spanish, expected: .init(value: "1.001")) try doTest("1,001", locale: us, expected: .init(value: "1001")) try doTest("1.001,45", locale: spanish, expected: .init(value: "1001.45"))