diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..40fa1db --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +## 0.3.4 + +* New Decimal128 Bson type. To be considered experimental. As in Dart there is not a corresponding Decimal128 type, we have used the Rational class (pub Rational), that allows to deal with rational numbers with no limits. +* The minimum SDK required has been raised to 2.7.0 diff --git a/README.md b/README.md index 96800bb..062f2bf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -bson +# bson + ========= [![Build Status](https://travis-ci.org/mongo-dart/bson.svg?branch=master)](https://travis-ci.org/mongo-dart/bson) diff --git a/lib/src/types/decimal_128.dart b/lib/src/types/decimal_128.dart index e9c805c..aae6f20 100644 --- a/lib/src/types/decimal_128.dart +++ b/lib/src/types/decimal_128.dart @@ -36,7 +36,8 @@ final Rational infinityValue = final Rational maxSignificand = Rational.fromInt(10).pow(34) - Rational.one; final Rational maxUInt64 = Rational.fromInt(2).pow(64); final Rational maxInt64 = Rational.fromInt(2).pow(63); -final Rational ten = Rational.fromInt(10); +final Rational _r10 = Rational.fromInt(10); +final Rational _r1 = Rational.fromInt(1); final Int64 maxExponent = Int64(12287); @@ -72,7 +73,7 @@ class BsonDecimal128 extends BsonObject { // NaN bin = BsonBinary.fromHexString('0000000000000000000000000000007c'); } else { - bin = convertDecimalToBinary(rational); + bin = convertRationalToBinary(rational); } } @@ -98,10 +99,10 @@ class BsonDecimal128 extends BsonObject { int get hashCode => bin.hexString.hashCode; bool operator ==(other) => other is BsonDecimal128 && toHexString() == other.toHexString(); - String toString() => 'Decimal128("${bin.hexString}")'; + String toString() => 'BsonDecimal128("${bin.hexString}")'; String toHexString() => bin.hexString; int get typeByte => bsonDecimal128; - Rational get value => convertBinaryToDecimal(bin); + Rational get value => convertBinaryToRational(bin); int byteLength() => 16; unpackValue(BsonBinary buffer) { @@ -132,7 +133,7 @@ class BsonDecimal128 extends BsonObject { } } - static Rational convertBinaryToDecimal(BsonBinary binary) { + static Rational convertBinaryToRational(BsonBinary binary) { Int64 high, low; if (binary.byteList == null) { binary.makeByteList(); @@ -192,10 +193,10 @@ class BsonDecimal128 extends BsonObject { significand = -significand; } - return significand * ten.power(exponent.toInt()); + return significand * _r10.power(exponent.toInt()); } - static BsonBinary convertDecimalToBinary(Rational rational) { + static BsonBinary convertRationalToBinary(Rational rational) { if (rational == null) { // Rational does not manage NaN return BsonBinary.fromHexString('0000000000000000000000000000007c'); @@ -207,9 +208,22 @@ class BsonDecimal128 extends BsonObject { return BsonBinary.fromHexString('000000000000000000000000000000f8'); } else if (rational == Rational.zero) { return BsonBinary.fromHexString('00000000000000000000000000004030'); + } else if (rational.hasFinitePrecision && + // if bigger than one (i.e at least one integer digit) + // we could have a lot of unnecessary trailing zeros calculated + // in the precision. + rational < _r1 && + rational.precision > 34) { + // Return zero + return BsonBinary.fromHexString('00000000000000000000000000004030'); } - String res = rational.toStringAsPrecisionFast(34); + String res; + if (rational.hasFinitePrecision) { + res = rational.toStringAsFixed(rational.scale); + } else { + res = rational.toStringAsPrecisionFast(34); + } int exponent = extractExponent(res); Rational significand = extractSignificand(res); @@ -383,7 +397,7 @@ extension RationalExtension on Rational { : value.toStringAsFixed(shiftExponent); } - /// Allows to calculate the power of numers even if the exponent is negative + /// Allows to calculate the power of numbers even if the exponent is negative Rational power(int exponent) => exponent.isNegative ? this.inverse.pow(-exponent) : this.pow(exponent); } diff --git a/test/bson_decimal_128_test_lib.dart b/test/bson_decimal_128_test_lib.dart index d78b940..324f37b 100644 --- a/test/bson_decimal_128_test_lib.dart +++ b/test/bson_decimal_128_test_lib.dart @@ -3,6 +3,8 @@ import 'package:rational/rational.dart'; import 'package:test/test.dart'; import 'package:bson/bson.dart'; +Rational ten = Rational.fromInt(10); + void runDecimal128() { group("Decimal 128:", () { group('Utils', () { @@ -69,9 +71,9 @@ void runDecimal128() { }); test("Compare", () { expect( - BsonDecimal128.convertBinaryToDecimal( + BsonDecimal128.convertBinaryToRational( BsonBinary.fromHexString('00e0ec5e0b6400000000000000002630')), - BsonDecimal128.convertBinaryToDecimal( + BsonDecimal128.convertBinaryToRational( BsonBinary.fromHexString('0b000000000000000000000000004030'))); }); }); @@ -122,6 +124,17 @@ void runDecimal128() { .hexString, 'ffffffff638e8d37c087adbe09edffff'); }); + test('Invalid Significand - return zero', () { + var r = Rational.parse("99999999999999999999999999999900000000001"); + expect(BsonDecimal128(r).bin.hexString, + '00000000000000000000000000004030'); + r = Rational.parse("9.9999999999999999999999999999900000000001"); + expect(BsonDecimal128(r).bin.hexString, + '0000000000000000000000000000f02f'); + r = Rational.parse("0.0099999999999999999999999999999900000000001"); + expect(BsonDecimal128(r).bin.hexString, + '00000000000000000000000000004030'); + }); }); group('Reading Official Rational Test', () {