Code Monkey home page Code Monkey logo

dart-rational's Introduction

Dart Rational

Build Status

This project enable to make computations on rational numbers.

Usage

To use this library in your code :

  • add a dependency in your pubspec.yaml :
dependencies:
  rational:

Rational numbers

  • add import in your dart code :
import 'package:rational/rational.dart';
  • Start computing using Rational.parse('1.23'), Rational(BigInt.from(12), BigInt.from(7)) or Rational.fromInt(12, 7).

License

Apache 2.0

dart-rational's People

Contributors

a14n avatar acoutts avatar dependabot[bot] avatar ilikerobots avatar kevmoo avatar krispypen avatar ovangle avatar rapzo avatar stevenroose avatar tomaszkubacki avatar twillouer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

dart-rational's Issues

BigInt.toDouble() calculates incorrectly

The BigInt toDouble() method is not calculating the nearest double correctly. Example:
expect(BigInt.parse("1").toDouble(), equals(1.0));
provides

Expected: <1.0>
  Actual: <2.0>

Inverse seems to be inaccurate and/or lose precision.

Given the sheer lack of issues opened on this project, I suspect that there's just something that I'm not fully understanding here, but this is what I'm running into:

// these values are known
var rate    = Rational.parse('12.863125781104889230980478192');
var inverse = Rational.parse( '0.077741601615132783190141925');

// however
print(   rate.inverse.toDecimalString()); //  0.0777416016
print(inverse.inverse.toDecimalString()); // 12.8631257811

// and
print((Rational.one / rate   ).toDecimalString()); //  0.0777416016
print((Rational.one / inverse).toDecimalString()); // 12.8631257811

// and
print(1 / rate.toDouble()   ); //  0.07774160161513279
print(1 / inverse.toDouble()); // 12.86312578110489

// and just to make it fun
print(   rate.inverse.inverse.toDecimalString()); //  0.077741601615132783190141925
print(inverse.inverse.inverse.toDecimalString()); // 12.863125781104889230980478192

Oddly, it seems that the inverses (whether internally calculated or externally) are losing a significant amount of precision. Stranger still is that while converting to doubles loses some precision, it not only keeps accuracy, but actually has higher precision than the Rational division. And then what really bakes my noodle is that even though rate.inverse loses precision, rate.inverse.inverse gains it all back 🤯

As I said at the start, given the lack of issues filed about anything, let alone this problem, I'm guessing there's something at play here that I'm not aware of / don't understand. Is that the case, or is this a bug? If this is correct behavior, could you explain how this works?

[request] Faster toStringAsPrecision() method

I was testing with big numbers the toStringAsPrecison() method, but I had poor performances.
In detail you can test numbers like:

Rational.parse("9.999999999999999999999999999999999E6144")
Rational.parse("9.999999999999999999999999999999999E-6144")
Rational.parse("8.888888888888888888888888888888888E-6144") /
      Rational.fromInt(3)

I tried to check if there were a faster alternative and I believe I got one.
Here is the extension I wrote:

extension RationalExtension on Rational {
  // If merged in the original source it is no more needed
  static final _r10 = Rational.fromInt(10);

  /// Provides a faster approach than the orginal method
  String toStringAsPrecisionFast(int requiredPrecision) {
    assert(requiredPrecision > 0);

    // this method is not able to manage rationals with infinite precision,
    // so we transform all numbers with infinite precision into
    // finite precision ones.
    // It is not important to calculate the exact precision of the new
    // number, what is fundament is that the new precision must not be lesser
    // than the required one.
    // The exact calculation will be done in the recursive call to
    // this method.
    if (!hasFinitePrecision) {
      /// in case of fractional digits starting with many zeros,
      /// we risk to loose precision, so, to compensate,
      /// we add the denominator precision.
      /// As there is not a precision method in the BigInt class
      /// we use the shortcut of creating a string and calculating
      /// the length. Maybe that there is a cleaner way,
      var pwr = requiredPrecision + denominator.toRadixString(10).length;
      var shifter = _r10.pow(pwr);
      var rational = (this * shifter).round() / shifter;
      return rational.toStringAsPrecisionFast(requiredPrecision);
    }

    /// The shift exponent is used to calculate the value of the number
    /// to round in order to loose precision (if exceeding the required one)
    ///
    /// `(precision - scale)`
    /// here we calculate how many digits and in which direction we have to
    /// move the fractional separator in order to obtain fields of the format
    /// `0.xxxxxxx`
    /// For example:
    ///              1230   125.78   0.0034
    ///              ^ (4)  ^ (3)    (-2)^
    ///             (4-0=>4)(5-2=>3)(2-4=>-2)
    ///              .1230  .12578   .34
    /// Now that we have the field in `0.xxxxx` format, we shift left for the
    /// number of required precision digits.
    /// For example:
    /// Precision -> 3
    ///              .1230  .12578   .34
    ///               123   125.78   340
    /// Precision -> 1
    ///              .1230  .12578   .34
    ///              1.230  1.2578   3.4
    /// As the required precision augments the exponent that we calculate,
    /// the previous calculation (precision - scale), that goes in the opposite
    /// direction, must be subtracted.
    /// The exponent for shifting the value is made by the formula:
    /// `requiredPrecision - (precision - scale)`
    var shiftExponent = requiredPrecision - (precision - scale);

    Rational value;
    if (shiftExponent == 0) {
      /// No shifting needed
      value = this;
    } else {
      /// given the exponent, we calculate the value to be used in order
      /// to shift our number
      var coefficient = _r10.power(shiftExponent);

      /// here we shift the number and round, so that we loose the non required
      /// precision digits (if any), and then we move back the digits in the
      /// opposite direction.
      value = (this * coefficient).round() / coefficient;
    }

    return shiftExponent <= 0
        ? value.toString()
        : value.toStringAsFixed(shiftExponent);
  }

  /// Allows to calculate the power of numers even if the exponent is negative
  Rational power(int exponent) =>
      exponent.isNegative ? this.inverse.pow(-exponent) : this.pow(exponent);
}

There is also a method that allows to calculate the power of a number with a negative exponent.

All tests I did seems to work fine, and the boost in performance is impressive.

Is it possible to update the 'toStringAsPrecision()` method and update the pow one in the Rational class with the solutions I provided?

Thanks in advance

Error in toDecimalString()

Hi.

I have certain Rational value that give me very wrong result for toDecimalString()
Example:

import 'package:rational/rational.dart';
main() {
  var r = new Rational(2260141353,1042850);
  print(r.toDecimalString());
  print(r.toDouble());
}

And output for this is

398.3956902997
2167.2736759840823

Second line is OK, first is very strange.

I use hosted version Decimal, rational_dart is on version 0.1.11
Dart VM version: 2.0.0-dev.35.0 (Fri Mar 9 13:09:14 2018 +0100) on "linux_x64"

Change of Copyright notice

Hello! According to the Appendix of Apache License 2.0, if you want to license your software under this License you should "attach the boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information". This condition is not met now.
Apache.org says:
Include a COPY of the Apache License, typically in a file called LICENSE, in your work, and consider also including a NOTICE file.

Сould you remove the copyright from the text of the license and add a COPYRIGHT NOTICE FILE (like this) in the appropriate form instead (including the year of the software development and your name and surname)?
Thank you in advance!

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Numbers starting with dot should be accepted

Hello, I think that numbers with format like .123 should be accepted as a valid number.
A lot of users are accustomed to write like that in input boxes.

See this example I took from chrome:

image

Even empty string is returning an exception but this is a valid number zero.

Strange error in Rational multiplication

It is probably some rare edge case - I use dart-rational for calculation in biggish dataset and got this first time:
I can reproduce this with dart-rational 0.2.0 and Dart VM version: 2.0.0-dev.42.0 (Unknown timestamp) on "linux_x64"

import 'package:rational/rational.dart';

main() {

  var r1 = new Rational(new BigInt.from(4403993), new BigInt.from(1000));
  var r2 = new Rational(new BigInt.from(1384700050), new BigInt.from(9498883));
  var r3 = r1 * r2; /// this line is breaking
  print(r3);

}

This snippet breaks with error

Unhandled exception:
RangeError (index): Index out of range: index should be less than 2: 2
#0      Uint32List.[]= (dart:typed_data-patch/dart:typed_data/typed_data_patch.dart:2465)
#1      _BigIntImpl._divRem (dart:core-patch/dart:core/bigint_patch.dart:1382)
#2      _BigIntImpl._rem (dart:core-patch/dart:core/bigint_patch.dart:1306)
#3      _BigIntImpl.% (dart:core-patch/dart:core/bigint_patch.dart:1697)
#4      _gcd (package:rational/rational.dart:34:11)
#5      new Rational (package:rational/rational.dart:87:17)
#6      Rational.* (package:rational/rational.dart:146:46)
#7      main (file:///home/vts/project/infovizion/fifo/fifo_simple/bin/ad_hoq.dart:8:15)
#8      _startIsolate.<anonymous closure> (dart:isolate-patch/dart:isolate/isolate_patch.dart:279)
#9      _RawReceivePortImpl._handleMessage (dart:isolate-patch/dart:isolate/isolate_patch.dart:165)

Add Rational.tryParse

Hi, great package 👋!

I'm just missing this complemental "constructor" for Rational.
Similar to what we already have with other primitive numbers, BigInt, and Decimal.

toStringWithPrecision() should not use toDouble()

toStringWithPrecision() uses toDouble() to first create a double type of the object. This means that very large numbers can be represented incorrectly using this method.

It should be possible to create a fully correct fixed-precision string using the internal numerator and denominator.

Process hanging in flutter due to simple computating

The following code runs well in dart, but hanging in flutter. Any idea what the problem is?

Rational.parse('91.4906445814007655705') + Rational.parse('0.24047904700217382')

My flutter/dart version:

Flutter 0.3.2 • channel beta • https://github.com/flutter/flutter.git
Framework • revision 44b7e7d3f4 (5 weeks ago) • 2018-04-20 01:02:44 -0700
Engine • revision 09d05a3891
Tools • Dart 2.0.0-dev.48.0.flutter-fe606f890b

My Device:

[ro.product.model]: [MI 5]
[ro.product.device]: [gemini]

Modulus calculation is not correct.

There is a problem with modulus calculation when the dividend and/or the divisor are negative numbers.

I attach a simple test file with a new method mod that could be a possible solution (useful to understand the logic) and a more compact one (mod2).

import 'package:rational/rational.dart';
import 'package:test/test.dart';

Rational mod(Rational one, Rational other) {
  Rational remainder = one.remainder(other);
  if (remainder == new Rational(0)) return remainder;
  if (remainder.isNegative) {
    if (other.isNegative) {
      return remainder;
    }
    return other + remainder;
  }
  if (other.isNegative) return other + remainder;
  return remainder;
}

Rational mod2(Rational one, Rational other) {
  Rational remainder = one.remainder(other);
  if (remainder.signum + other.signum == 0) return other + remainder;
  return remainder;
}

main() {

  test('test Modulus', () {
    expect(new Rational(5) % new Rational(4), equals(new Rational(1)));
    expect(new Rational(-5) % new Rational(4), equals(new Rational(3)));
    expect(new Rational(5) % new Rational(-4), equals(new Rational(-3)));
    expect(new Rational(-5) % new Rational(-4), equals(new Rational(-1)));
    expect(new Rational(4) % new Rational(4), equals(new Rational(0)));
    expect(new Rational(-4) % new Rational(4), equals(new Rational(0)));
    expect(new Rational(4) % new Rational(-4), equals(new Rational(0)));
    expect(new Rational(-4) % new Rational(-4), equals(new Rational(0)));
  });

  test('test new version', () {
    expect(mod(new Rational(5), new Rational(4)), equals(new Rational(1)));
    expect(mod(new Rational(-5), new Rational(4)), equals(new Rational(3)));
    expect(mod(new Rational(5), new Rational(-4)), equals(new Rational(-3)));
    expect(mod(new Rational(-5), new Rational(-4)), equals(new Rational(-1)));
    expect(mod(new Rational(4), new Rational(4)), equals(new Rational(0)));
    expect(mod(new Rational(-4), new Rational(4)), equals(new Rational(0)));
    expect(mod(new Rational(4), new Rational(-4)), equals(new Rational(0)));
    expect(mod(new Rational(-4), new Rational(-4)), equals(new Rational(0)));
  });


  test('test new Version "compact"', () {
    expect(mod2(new Rational(5), new Rational(4)), equals(new Rational(1)));
    expect(mod2(new Rational(-5), new Rational(4)), equals(new Rational(3)));
    expect(mod2(new Rational(5), new Rational(-4)), equals(new Rational(-3)));
    expect(mod2(new Rational(-5), new Rational(-4)), equals(new Rational(-1)));
    expect(mod2(new Rational(4), new Rational(4)), equals(new Rational(0)));
    expect(mod2(new Rational(-4), new Rational(4)), equals(new Rational(0)));
    expect(mod2(new Rational(4), new Rational(-4)), equals(new Rational(0)));
    expect(mod2(new Rational(-4), new Rational(-4)), equals(new Rational(0)));
  });


}

Do we need to import this package when using Decimal package?

Rational seems to be already a dependency in Decimal package, so in our projects, we should not need to explicitly put rational in our pubspec.yaml
However in our project, importing decimal is not enough to create Rationals.
import 'package:decimal/decimal.dart';
So you think this is a pub/cache issue on my end?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.