Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid crashing for RETDecimal.max().formated(...) #1044

Merged
merged 1 commit into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion RadixWallet/EngineKit/RETDecimal+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,23 @@ extension RETDecimal {
let integerCount = UInt(Swift.max(digits.count - RETDecimal.scale, 1))
if integerCount > totalPlaces {
let scale = RETDecimal(exponent: integerCount - totalPlaces)
return (self / scale).rounded(decimalPlaces: 0) * scale
let base = self / scale
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say that we don't want to protect ourselves against any kind of error here, only the specific case of max, no? We could just

guard self != max else {
  // return the number rounded towards zero
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No because if the value is close to max we don't want to fail either. Hmm

let decimalPlaces: UInt = 0
let base_rounded = base.rounded(decimalPlaces: decimalPlaces)

do {
return try base_rounded.mul(other: scale)
} catch {
// we overflowed which will happen if we are using RETDecimal.max()
// instead of using `base.rounded(decimalPlaces: 0)` which uses
// rounding mode `toNearestMidpointAwayFromZero` we will use
// `toZero` to avoid overflow.
let safeBase = try! base.round(
decimalPlaces: Int32(decimalPlaces),
roundingMode: .toZero
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

N.B. .toNearestMidpointTowardZero will not work either, must be .toZero.

)
return safeBase * scale
}
} else {
// The remaining digits are decimals and we keep up to totalPlaces of them
let decimalsToKeep = totalPlaces - integerCount
Expand Down
8 changes: 5 additions & 3 deletions RadixWalletTests/EngineKitTests/DecimalTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -274,15 +274,17 @@ final class DecimalTests: TestCase {
}

func test_format_decimal() throws {
func doTest(_ decimalString: String, expected: String, line: UInt = #line) throws {
func doTest(_ decimal: RETDecimal, expected: String, line: UInt = #line) throws {
let locale = Locale(identifier: "en_US_POSIX")
let decimal = try RETDecimal(value: decimalString)
let actual = decimal.formatted(locale: locale, totalPlaces: 8, useGroupingSeparator: false)
XCTAssertEqual(actual, expected, line: line)
}
func doTest(_ decimalString: String, expected: String, line: UInt = #line) throws {
try doTest(RETDecimal(value: decimalString), expected: expected, line: line)
}

try doTest(RETDecimal.max(), expected: "3.138e39")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a new test, which fails on main branch, but not anymore.

try doTest("0.009999999999999", expected: "0.01")

try doTest("12341234", expected: "12.341234 M")
try doTest("1234123.4", expected: "1.2341234 M")
try doTest("123456.34", expected: "123456.34")
Expand Down
Loading