Skip to content

Commit

Permalink
Better invalid significant test on Decimal 128
Browse files Browse the repository at this point in the history
  • Loading branch information
Giorgio committed Oct 17, 2020
1 parent cd5fa62 commit ff640bf
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 12 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
32 changes: 23 additions & 9 deletions lib/src/types/decimal_128.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -72,7 +73,7 @@ class BsonDecimal128 extends BsonObject {
// NaN
bin = BsonBinary.fromHexString('0000000000000000000000000000007c');
} else {
bin = convertDecimalToBinary(rational);
bin = convertRationalToBinary(rational);
}
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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');
Expand All @@ -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);

Expand Down Expand Up @@ -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);
}
17 changes: 15 additions & 2 deletions test/bson_decimal_128_test_lib.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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', () {
Expand Down Expand Up @@ -69,9 +71,9 @@ void runDecimal128() {
});
test("Compare", () {
expect(
BsonDecimal128.convertBinaryToDecimal(
BsonDecimal128.convertBinaryToRational(
BsonBinary.fromHexString('00e0ec5e0b6400000000000000002630')),
BsonDecimal128.convertBinaryToDecimal(
BsonDecimal128.convertBinaryToRational(
BsonBinary.fromHexString('0b000000000000000000000000004030')));
});
});
Expand Down Expand Up @@ -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', () {
Expand Down

0 comments on commit ff640bf

Please sign in to comment.