Skip to content

Commit

Permalink
RDART-866: kn/decimal128 web support (#1713)
Browse files Browse the repository at this point in the history
* Implement Decimal128 for web

* fromDouble

* ==, hashCode

* Add deps on decimal and rational

* Match IEEE 754 Decimal128 precision (+/- 10^6144)

* Comment on compliancy

* Fix compile for web regression

* Update CHANGELOG
  • Loading branch information
nielsenko authored Jun 7, 2024
1 parent 558ab3d commit 889e434
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 7 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"fnum",
"geospatial",
"HRESULT",
"intf",
"keepalive",
"keypaths",
"loggable",
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
### Enhancements
* Report the originating error that caused a client reset to occur. (Core 14.9.0)
* Allow the realm package, and code generated by realm_generator to be included when building
for web without breaking compilation. (Issue [#1374](https://github.com/realm/realm-dart/issues/1374))
for web without breaking compilation. (Issue [#1374](https://github.com/realm/realm-dart/issues/1374),
PR [#1713](https://github.com/realm/realm-dart/pull/1713))

### Fixed
* `Realm.writeAsync` did not handle async callbacks (`Future<T> Function()`) correctly. (Issue [#1667](https://github.com/realm/realm-dart/issues/1667))
Expand Down
88 changes: 82 additions & 6 deletions packages/realm_dart/lib/src/handles/web/decimal128.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
// Copyright 2024 MongoDB, Inc.
// SPDX-License-Identifier: Apache-2.0

import '../decimal128.dart' as intf;
import 'package:decimal/decimal.dart';

import 'package:realm_dart/src/convert.dart';

import 'web_not_supported.dart';

import '../decimal128.dart' as intf;

/// This is not a compliant IEEE 754-2008 Decimal128 implementation, as it is
/// just based on the [decimal](https://pub.dev/packages/decimal) package.
/// Which is based on the [rational](https://pub.dev/packages/rational) package,
/// which is again based on the [BigInt] class.
///
/// The issues are mostly in some of the odd corner cases of IEEE 754-2008
/// Decimal128, such as:
/// * -0 < 0,
/// * NaN != NaN,
/// * 1 / 0 = Inf, etc.
///
/// Also, be warned that this class is incredible slow compared to the native
/// implementation.
class Decimal128 implements intf.Decimal128 {
static final zero = Decimal128.fromInt(0);

Expand All @@ -23,17 +41,75 @@ class Decimal128 implements intf.Decimal128 {
static final negativeInfinity = -infinity;

/// Parses a string into a [Decimal128]. Returns `null` if the string is not a valid [Decimal128].
static Decimal128? tryParse(String source) => webNotSupported();
static Decimal128? tryParse(String source) => Decimal.tryParse(source).convert(Decimal128._);

/// Parses a string into a [Decimal128]. Throws a [FormatException] if the string is not a valid [Decimal128].
factory Decimal128.parse(String source) => webNotSupported();
factory Decimal128.parse(String source) => Decimal128._(Decimal.parse(source));

/// Converts a `int` into a [Decimal128].
factory Decimal128.fromInt(int value) => webNotSupported();
factory Decimal128.fromInt(int value) => Decimal128._(Decimal.fromInt(value));

/// Converts a `double` into a [Decimal128].
factory Decimal128.fromDouble(double value) => webNotSupported();
factory Decimal128.fromDouble(double value) {
if (value.isNaN) return nan;
if (value.isInfinite) return value.isNegative ? negativeInfinity : infinity;
return Decimal128._(Decimal.parse(value.toString()));
}

final Decimal _value;
Decimal128._(Decimal value) : _value = value.truncate(scale: 6144);

@override
Decimal128 operator *(covariant Decimal128 other) => Decimal128._(_value * other._value);

@override
Decimal128 operator +(covariant Decimal128 other) => Decimal128._(_value + other._value);

@override
Decimal128 operator -() => Decimal128._(-_value);

@override
Decimal128 operator -(covariant Decimal128 other) => Decimal128._(_value - other._value);

// Note IEEE 754 Decimal128 defines division with zero as infinity, similar to double
@override
Decimal128 operator /(covariant Decimal128 other) => Decimal128._((_value / other._value).toDecimal(scaleOnInfinitePrecision: 6144));

@override
bool operator <(covariant Decimal128 other) => _value < other._value;

@override
bool operator <=(covariant Decimal128 other) => _value <= other._value;

@override
bool operator >(covariant Decimal128 other) => _value > other._value;

@override
bool operator >=(covariant Decimal128 other) => _value >= other._value;

@override
Decimal128 abs() => Decimal128._(_value.abs());

@override
int compareTo(covariant Decimal128 other) {
final sign = _value.compareTo(other._value);
if (sign < 0) return -1;
if (sign > 0) return 1;
return 0;
}

@override
bool get isNaN => this == nan;

@override
int toInt() => _value.toBigInt().toInt();

@override
String toString() => _value.toStringAsExponential(27);

@override
operator ==(Object other) => other is Decimal128 && _value == other._value;

@override
noSuchMethod(Invocation invocation) => webNotSupported();
int get hashCode => _value.hashCode;
}
2 changes: 2 additions & 0 deletions packages/realm_dart/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ dependencies:
build_runner: ^2.1.0
http: ^1.0.0
cancellation_token: ^2.0.0
decimal: ^3.0.1
rational: ^2.2.3

dev_dependencies:
build_cli: ^2.2.2
Expand Down

0 comments on commit 889e434

Please sign in to comment.