fraction package logo

A package that helps you work with fractions and mixed fractions.

Code coverage CI status Stars count on GitHub Latest version on pub.dev

🌎 https://pub.dev/packages/fraction


Working with fractions

You can create new Fraction objects using one of its constructors:

  • Default: requires the numerator and, optionally, the denominator (which defaults to 1).

    Fraction(3, 5); // 3/5
    Fraction(3, 1); // 3
    
  • fromString: requires a String that represents a fraction.

    Fraction.fromString("2/4"); // 2/4
    Fraction.fromString("-2/4"); // -2/4
    Fraction.fromString("2/-4"); // Throws a FractionException
    Fraction.fromString("-2"); // -2
    Fraction.fromString("/3"); // Throws a FractionException
    
  • fromDouble: creates a Fraction from a double value. Irrational numbers cannot be represented as fractions by definition, so the constructor has a precision parameter that controls how close the approximation must be to the original value.

    Fraction.fromDouble(1.5); // 3/2
    Fraction.fromDouble(-8.5); // -17/2
    Fraction.fromDouble(math.pi); // 1146408/364913
    Fraction.fromDouble(math.pi, precision: 1.0e-4); // 333/106
    

    For example, pi cannot be represented exactly as a fraction because it's an irrational number. The constructor stops as soon as the result is within precision of the given value.

Thanks to extension methods, you can also create a Fraction object "on the fly" by calling toFraction() on a number or a string:

5.toFraction(); // 5
1.5.toFraction(); // 3/2
"6/5".toFraction(); // 6/5

The Fraction type is immutable, so methods that would change the object's internal state return a new instance instead. For example, the reduce() method reduces the fraction to its lowest terms and returns a new object:

final fraction = Fraction.fromString("12/20"); // 12/20
final reduced = fraction.reduce(); // 3/5

Fractions can be converted to and from Unicode glyphs, when a matching glyph exists. For example:

Fraction.fromGlyph("¼"); // 1/4
Fraction(1, 2).toStringAsGlyph(); // "½"

You can add, subtract, multiply, and divide fractions using the arithmetic operators. Results are always in lowest terms:

final f1 = Fraction(5, 7); // 5/7
final f2 = Fraction(1, 5); // 1/5

final sum = f1 + f2; // 32/35
final sub = f1 - f2; // 18/35
final mul = f1 * f2; // 1/7
final div = f1 / f2; // 25/7

The Fraction type has a rich API that covers the most common operations you'd expect to perform on a fraction:

Fraction(10, 2).toDouble();  // 5.0
Fraction(10, 2).inverse();   // 2/10
Fraction(1, 15).isWhole;     // false
Fraction(2, 3).negate();     // -2/3
Fraction(1, 15).isImproper;  // false
Fraction(1, 15).isProper;    // true

// Access the numerator and denominator by index
final fraction = Fraction(-7, 12);

print('${fraction[0]}'); // -7
print('${fraction[1]}'); // 12

In the last example, any index other than 0 or 1 throws a FractionException. Two fractions are equal if they represent the same value. For example, 1/2 and 3/6 are equal because 3/6 reduces to 1/2, and they also have the same hashCode, so they can safely be used in sets and maps.

Working with mixed fractions

A mixed fraction is made up of a whole part and a proper fraction (a fraction whose numerator is smaller than its denominator). Building a MixedFraction object is straightforward:

MixedFraction(
  whole: 3,
  numerator: 4,
  denominator: 7,
);

As with the Fraction type, you can use various named constructors:

MixedFraction.fromDouble(1.5);
MixedFraction.fromString("1 1/2");

You can also create new MixedFraction objects using extension methods:

final mixed = "1 1/2".toMixedFraction();

The whole, numerator and denominator values of a mixed fraction are never negative: the sign is stored separately and exposed by isNegative. A minus sign on either the whole part or the fractional part makes the whole value negative:

final a = MixedFraction(whole: -2, numerator: 1, denominator: 3); // -2 1/3
final b = MixedFraction(whole: 2, numerator: -1, denominator: 3); // -2 1/3
final c = MixedFraction(whole: 0, numerator: -1, denominator: 2); // -1/2

a.whole;      // 2
a.isNegative; // true
a.toDouble(); // -2.3333333333333335

MixedFraction.fromString("-2 1/3"); // -2 1/3
MixedFraction.fromString("-0 1/2"); // -1/2

If both parts are negative (for example, MixedFraction(whole: -2, numerator: -1, denominator: 3) or "-2 -1/3"), a MixedFractionException is thrown because the intended sign is ambiguous.

Mixed fractions support the same arithmetic operators as fractions:

final m1 = MixedFraction(whole: 5, numerator: 7, denominator: 8); // 5 7/8
final m2 = MixedFraction(whole: 3, numerator: 1, denominator: 9); // 3 1/9

final sum = m1 + m2; // 8 71/72
final sub = m1 - m2; // 2 55/72
final mul = m1 * m2; // 18 5/18
final div = m1 / m2; // 1 199/224

Like Fraction, the MixedFraction type is immutable, so its internal state is guaranteed never to change. Check out the official API documentation on pub.dev for a complete overview.

Egyptian fractions

An Egyptian fraction is a finite sum of distinct unit fractions: fractions whose numerator is 1 and whose denominator is a positive integer. For example:

  • 5/8 = 1/2 + 1/8 (where "1/2 + 1/8" is the Egyptian fraction)

In other words, an Egyptian fraction represents a fraction as a sum of fractions of the form 1/x. If the value is 1 or greater, its whole part comes first as a single term. Here's how to compute one:

final egyptianFraction1 = Fraction(5, 8).toEgyptianFraction();
print(egyptianFraction1.join(" + ")); // prints "1/2 + 1/8"

final egyptianFraction2 = MixedFraction(
  whole: 2,
  numerator: 4,
  denominator: 5,
).toEgyptianFraction();
print(egyptianFraction2.join(" + ")); // prints "2 + 1/2 + 1/4 + 1/20"

The toEgyptianFraction() method returns a List<Fraction>. It throws a FractionException if the value is negative, or if a denominator is too large to fit in an int.

Notes

Fraction and MixedFraction are both subtypes of Rational, which lets you parse either kind of fraction with a single method call. For example:

// Returns a 'Fraction' object
Rational.tryParse('1/5'); // 1/5

// Returns a 'MixedFraction' object
Rational.tryParse('2 4/7'); // 2 4/7

// Returns 'null' because the string isn't a valid fraction or mixed fraction
Rational.tryParse(''); // null

Parsing an integer value, such as Rational.tryParse('3'), always returns a Fraction.

Libraries

fraction
Create, parse and do arithmetic with fractions and mixed fractions, convert them to and from numbers and strings, and compute Egyptian fraction expansions.