diff --git a/CHANGELOG.md b/CHANGELOG.md index 686c56f..9583013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## 0.6.0 +- Fix static analysis issues +- Add support for Dart 3 +- Remove internal members that were exposed to the API + ## 0.5.0 - Add toPlainString() diff --git a/analysis_options.yaml b/analysis_options.yaml index f95c275..f2777f8 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -4,3 +4,7 @@ include: package:lints/recommended.yaml analyzer: exclude: - example/**/*.dart + +linter: + rules: + - public_member_api_docs \ No newline at end of file diff --git a/example/main.dart b/example/main.dart new file mode 100644 index 0000000..bbe8618 --- /dev/null +++ b/example/main.dart @@ -0,0 +1,5 @@ +import 'package:big_decimal/big_decimal.dart'; + +final decimal1 = BigDecimal.parse('1'); +final decimal2 = BigDecimal.parse('2'); +final decimal3 = decimal1 + decimal2; // 3 diff --git a/lib/src/big_decimal.dart b/lib/src/big_decimal.dart index 07c3df5..cf5752c 100644 --- a/lib/src/big_decimal.dart +++ b/lib/src/big_decimal.dart @@ -1,30 +1,51 @@ +// enum is using screaming snake case due to a direct migration from java // ignore_for_file: constant_identifier_names +/// rounding mode used when doing operations on a [BigDecimal] enum RoundingMode { + /// away from zero UP, + + /// towards zero DOWN, + + /// towards +infinity CEILING, + + /// towards -infinity FLOOR, + + /// away from zero if remainder comparison to half divisor is even HALF_UP, + + /// towards zero if remainder comparison to half divisor is even HALF_DOWN, + + /// towards zero if remainder comparison to half divisor is even and [BigDecimal] is odd HALF_EVEN, + + /// does not round at all. + /// + /// Throws [Exception] if not exact and needs rounding UNNECESSARY, } -const plusCode = 43; -const minusCode = 45; -const dotCode = 46; -const smallECode = 101; -const capitalECode = 69; -const zeroCode = 48; -const nineCode = 57; +const _plusCode = 43; +const _minusCode = 45; +const _dotCode = 46; +const _smallECode = 101; +const _capitalECode = 69; +const _zeroCode = 48; +const _nineCode = 57; +/// representation of an arbitrarily large decimal number class BigDecimal implements Comparable { BigDecimal._({ required this.intVal, required this.scale, }); + /// factory constructor of a [BigDecimal] from a [BigInt] factory BigDecimal.fromBigInt(BigInt value) { return BigDecimal._( intVal: value, @@ -32,21 +53,27 @@ class BigDecimal implements Comparable { ); } - static BigDecimal zero = BigDecimal.fromBigInt(BigInt.zero); - static BigDecimal one = BigDecimal.fromBigInt(BigInt.one); - static BigDecimal two = BigDecimal.fromBigInt(BigInt.two); + /// a [BigDecimal] with a numerical value of 0 + static final zero = BigDecimal.fromBigInt(BigInt.zero); + + /// a [BigDecimal] with a numerical value of 1 + static final one = BigDecimal.fromBigInt(BigInt.one); + + /// a [BigDecimal] with a numerical value of 2 + static final two = BigDecimal.fromBigInt(BigInt.two); - static int nextNonDigit(String value, [int start = 0]) { + static int _nextNonDigit(String value, [int start = 0]) { var index = start; for (; index < value.length; index++) { final code = value.codeUnitAt(index); - if (code < zeroCode || code > nineCode) { + if (code < _zeroCode || code > _nineCode) { break; } } return index; } + /// try to create a [BigDecimal] from a [String]. Returns null if invalid static BigDecimal? tryParse(String value) { try { return BigDecimal.parse(value); @@ -55,24 +82,27 @@ class BigDecimal implements Comparable { } } + /// try to create a [BigDecimal] from a [String]. + /// + /// Throws [Exception] if invalid factory BigDecimal.parse(String value) { var sign = ''; var index = 0; var nextIndex = 0; switch (value.codeUnitAt(index)) { - case minusCode: + case _minusCode: sign = '-'; index++; break; - case plusCode: + case _plusCode: index++; break; default: break; } - nextIndex = nextNonDigit(value, index); + nextIndex = _nextNonDigit(value, index); final integerPart = '$sign${value.substring(index, nextIndex)}'; index = nextIndex; @@ -81,9 +111,9 @@ class BigDecimal implements Comparable { } var decimalPart = ''; - if (value.codeUnitAt(index) == dotCode) { + if (value.codeUnitAt(index) == _dotCode) { index++; - nextIndex = nextNonDigit(value, index); + nextIndex = _nextNonDigit(value, index); decimalPart = value.substring(index, nextIndex); index = nextIndex; @@ -96,8 +126,8 @@ class BigDecimal implements Comparable { } switch (value.codeUnitAt(index)) { - case smallECode: - case capitalECode: + case _smallECode: + case _capitalECode: index++; final exponent = int.parse(value.substring(index)); return BigDecimal._( @@ -112,38 +142,57 @@ class BigDecimal implements Comparable { ); } + /// the arbitrarily large numeric value without scale final BigInt intVal; + + /// precision of the decimal digits of this late final int precision = _calculatePrecision(); + + /// scale of this [BigDecimal] final int scale; @override - bool operator ==(dynamic other) => + bool operator ==(Object other) => other is BigDecimal && compareTo(other) == 0; - bool exactlyEquals(dynamic other) => + /// compares this with [other] for both value and scale + bool exactlyEquals(Object? other) => other is BigDecimal && intVal == other.intVal && scale == other.scale; + /// adds this to [other] BigDecimal operator +(BigDecimal other) => _add(intVal, other.intVal, scale, other.scale); + /// multiply this with [other] BigDecimal operator *(BigDecimal other) => BigDecimal._(intVal: intVal * other.intVal, scale: scale + other.scale); + /// subtracts this to [other] BigDecimal operator -(BigDecimal other) => _add(intVal, -other.intVal, scale, other.scale); + /// Whether this is less than [other]. bool operator <(BigDecimal other) => compareTo(other) < 0; + /// Whether this is less than or equal to [other]. bool operator <=(BigDecimal other) => compareTo(other) <= 0; + /// Whether this is greater than [other]. bool operator >(BigDecimal other) => compareTo(other) > 0; + /// Whether this is greater than or equal to [other]. bool operator >=(BigDecimal other) => compareTo(other) >= 0; + /// Negates this big decimal BigDecimal operator -() => BigDecimal._(intVal: -intVal, scale: scale); + /// Returns the absolute value of this BigDecimal abs() => BigDecimal._(intVal: intVal.abs(), scale: scale); + /// divides this number by [divisor]. Defaults to not rounding the number. + /// + /// Throws [Exception] if rounding is [RoundingMode.UNNECESSARY] but rounding + /// is actually necessary. BigDecimal divide( BigDecimal divisor, { RoundingMode roundingMode = RoundingMode.UNNECESSARY, @@ -152,6 +201,7 @@ class BigDecimal implements Comparable { _divide(intVal, this.scale, divisor.intVal, divisor.scale, scale ?? this.scale, roundingMode); + /// this to the power of [n] BigDecimal pow(int n) { if (n >= 0 && n <= 999999999) { // TODO: Check scale of this multiplication @@ -162,13 +212,29 @@ class BigDecimal implements Comparable { 'Invalid operation: Exponent should be between 0 and 999999999'); } + /// returns this as a [double] double toDouble() => intVal.toDouble() / BigInt.from(10).pow(scale).toDouble(); + + /// returns this as a [BigInt] with the desired [roundingMode] + /// + /// Throws [Exception] if rounding is [RoundingMode.UNNECESSARY] but rounding + /// is actually necessary. BigInt toBigInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) => withScale(0, roundingMode: roundingMode).intVal; + + /// returns this as a [int] with the desired [roundingMode] + /// + /// Throws [Exception] if rounding is [RoundingMode.UNNECESSARY] but rounding + /// is actually necessary. int toInt({RoundingMode roundingMode = RoundingMode.UNNECESSARY}) => toBigInt(roundingMode: roundingMode).toInt(); + /// returns a new [BigDecimal] with the desired [newScale]. May round by + /// [roundingMode]. + /// + /// Throws [Exception] if rounding is [RoundingMode.UNNECESSARY] but rounding + /// is actually necessary. BigDecimal withScale( int newScale, { RoundingMode roundingMode = RoundingMode.UNNECESSARY, @@ -179,11 +245,11 @@ class BigDecimal implements Comparable { return BigDecimal._(intVal: BigInt.zero, scale: newScale); } else { if (newScale > scale) { - final drop = sumScale(newScale, -scale); + final drop = _sumScale(newScale, -scale); final intResult = intVal * BigInt.from(10).pow(drop); return BigDecimal._(intVal: intResult, scale: newScale); } else { - final drop = sumScale(scale, -newScale); + final drop = _sumScale(scale, -newScale); return _divideAndRound(intVal, BigInt.from(10).pow(drop), newScale, roundingMode, newScale); } @@ -223,14 +289,14 @@ class BigDecimal implements Comparable { if (dividend == BigInt.zero) { return BigDecimal._(intVal: BigInt.zero, scale: scale); } - if (sumScale(scale, divisorScale) > dividendScale) { + if (_sumScale(scale, divisorScale) > dividendScale) { final newScale = scale + divisorScale; final raise = newScale - dividendScale; final scaledDividend = dividend * BigInt.from(10).pow(raise); return _divideAndRound( scaledDividend, divisor, scale, roundingMode, scale); } else { - final newScale = sumScale(dividendScale, -scale); + final newScale = _sumScale(dividendScale, -scale); final raise = newScale - divisorScale; final scaledDivisor = divisor * BigInt.from(10).pow(raise); return _divideAndRound( @@ -258,14 +324,14 @@ class BigDecimal implements Comparable { return BigDecimal._(intVal: quotient, scale: scale); } else { if (preferredScale != scale) { - return createAndStripZerosForScale(quotient, scale, preferredScale); + return _createAndStripZerosForScale(quotient, scale, preferredScale); } else { return BigDecimal._(intVal: quotient, scale: scale); } } } - static BigDecimal createAndStripZerosForScale( + static BigDecimal _createAndStripZerosForScale( BigInt intVal, int scale, int preferredScale, @@ -284,7 +350,7 @@ class BigDecimal implements Comparable { break; } intValMut = intValMut ~/ ten; - scaleMut = sumScale(scaleMut, -1); + scaleMut = _sumScale(scaleMut, -1); } return BigDecimal._(intVal: intValMut, scale: scaleMut); @@ -334,11 +400,6 @@ class BigDecimal implements Comparable { } } - static int sumScale(int scaleA, int scaleB) { - // TODO: We need to check for overflows here - return scaleA + scaleB; - } - @override int compareTo(BigDecimal other) { if (scale == other.scale) { @@ -394,6 +455,7 @@ class BigDecimal implements Comparable { return b.toString(); } + /// returns its [String] represantation without using exponential notation String toPlainString() { if (scale == 0) { return intVal.toString(); @@ -423,3 +485,8 @@ class BigDecimal implements Comparable { return b.toString(); } } + +int _sumScale(int scaleA, int scaleB) { + // TODO: We need to check for overflows here + return scaleA + scaleB; +} diff --git a/pubspec.yaml b/pubspec.yaml index 9a29ff5..333254c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,12 +1,12 @@ name: big_decimal -version: 0.5.0 +version: 0.6.0 description: > A bugless implementation of BigDecimal in Dart based on Java's BigDecimal repository: https://github.com/bugless/big-decimal environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.12.0 <4.0.0" dev_dependencies: - lints: ^2.0.1 + lints: ^5.1.1 test: ^1.17.2 diff --git a/test/big_decimal_test.dart b/test/big_decimal_test.dart index 3090c1e..46722ab 100644 --- a/test/big_decimal_test.dart +++ b/test/big_decimal_test.dart @@ -287,9 +287,9 @@ void main() { String down, String ceiling, String floor, - String half_up, - String half_down, - String half_even, + String halfUp, + String halfDown, + String halfEven, Object unnecessary, ) { BigDecimal round(RoundingMode mode) => @@ -300,11 +300,11 @@ void main() { reason: 'CEILING'); expect(round(RoundingMode.FLOOR), exactly(floor.dec), reason: 'FLOOR'); - expect(round(RoundingMode.HALF_UP), exactly(half_up.dec), + expect(round(RoundingMode.HALF_UP), exactly(halfUp.dec), reason: 'HALF_UP'); - expect(round(RoundingMode.HALF_DOWN), exactly(half_down.dec), + expect(round(RoundingMode.HALF_DOWN), exactly(halfDown.dec), reason: 'HALF_DOWN'); - expect(round(RoundingMode.HALF_EVEN), exactly(half_even.dec), + expect(round(RoundingMode.HALF_EVEN), exactly(halfEven.dec), reason: 'HALF_EVEN'); if (unnecessary is String) { expect(round(RoundingMode.UNNECESSARY), exactly(unnecessary.dec),