Fraction type documentation

Syntax highlighting is disabled until I can figure out how to comply with GDPR (it is external content after all). The source still has it enabled and the relevant lines are commented out on this page (you could uncomment them with Dev Tools, but that's none of my responsibility...)

Basics

Basic UsageTop

Here's a basic program demonstrating the easy use of the Fraction class. Assume you'd like to write the mathematical function f(x)=1/x. In a normal program, you'd write

    double f(int x)
    
But what about things like f(f(f(x)))? Okay, we could do

    double f(double x)
    
But then you'd have the topic of floating-point accuracy which means your code will get inaccurate with large numbers. So, how do we solve this? With the Fraction class. Just replace all your int, double, float, etc. with the keyword Fraction.

    #include <cstdio>

    #include "npasson_fraction.hpp"
    using npasson::Fraction;

    Fraction f(Fraction x) {
        return 1/x;
    }

    double d(double x) {
        return 1/x;
    }

    int main() {
        // for f(x)=1/x, it needs to hold that x == f(f(x)) == f(f(f(f(x)))) and so on.
        Fraction a = 1'000'000'000'000'000;
        double   b = 1'000'000'000'000'000;

        if (a == f(f(a)))               printf("Fraction Test 1 holds\n");
        if (a == f(f(f(f(a)))))         printf("Fraction Test 2 holds\n");
        if (a == f(f(f(f(f(f(a)))))))   printf("Fraction Test 3 holds\n");

        if (a == d(d(b)))               printf("Double Test 1 holds\n");
        if (a == d(d(d(d(b)))))         printf("Double Test 2 holds\n");
        if (a == d(d(d(d(d(d(b)))))))   printf("Double Test 3 holds\n");
    }
    
Compiled with

    g++ -std=c++17 -Wall -O0
    
the program outputs

    Fraction Test 1 holds
    Fraction Test 2 holds
    Fraction Test 3 holds

    Process finished with exit code 0
    
As demonstrated in the example, you can treat Fraction like a normal numeric type. You can initialize it in many different ways, see Constructors, and also use it in most calculations, see Operators.

Supported typesTop

The following types are completely supported in all operations with Fractions: The following types are partially supported:

ConstructorsTop

The following constructors exist:

    Fraction()
    Fraction(Fraction)
    Fraction(long long signed int, long long signed int);
    Fraction(short)
    Fraction(unsigned short)
    Fraction(int)
    Fraction(unsigned int)
    Fraction(long int)
    Fraction(unsigned long int)
    Fraction(long long int)
    Fraction(unsigned long long int)
    Fraction(float)
    Fraction(double)
    Fraction(long double)
    Fraction(bool)
    

Implicit constructors

Implicit construction is allowed for the following types: You can use these types like so:

    Fraction a = 5;
    Fraction b = 78236487263874622ll;
    Fraction c = 3.7;
    
These types can also be used in place of Fraction types, as implicit conversion will take care of them.

(Mathematical) fraction constructor


    Fraction(long long signed int, long long signed int);
    
is the fraction constructor. It is used like Fraction(a,b) which will create a Fraction a/b. This is best used for non-integer values which need to retain accuracy.

Explicit constructors

For all other types, an explicit constructor is required.

    Fraction a(26.3f); 	// float
    Fraction b("1.3"); 	// std::string
    Fraction c(a); 		// Fraction
    Fraction d("2,5"); 	// std::string with European decimal place
    const char* temp = "3.7";
    Fraction f(temp); 	// const char*
    

Bool constructor

The bool constructor works as following. Given a true, it is functionally equivalent to an empty constructor:

    Fraction(true) == Fraction()
    
Given a false, it constructs a Fraction where the Fraction::_invalid bit is triggered. This bit indicates an invalid Fraction. Invalid fractions have undefined behavior on all operations. Validity can and should be checked via valid():

    Fraction f(false);

    if (f.valid()) {
        // will not execute
    }
    
There also exists the macro INVALID_FRACTION, which is defined as Fraction(false) and may be returned by functions. It is not safe to check against this macro to ensure validity.

DestructorsTop

There exists only one destructor:

    ~Fraction() = default;
    

Operations

Every operator listed here can either be used between two Fractions, or a Fraction and a type listed in the Supported types list.

Arithmetic operatorsTop

The following arithmetic operators exist: With T a type from the supported types list, the operators return according to the following list:

Comparison operatorsTop

The following comparison operators exist: When comparing, the usual math rules apply, that means 1/2 == 2/4 == 30/60 holds, as does 1/2 > 1/4 > 3/16

Unary operatorsTop

Assuming Fraction f, the following unary operators exist:

The unary + operator exists for some reason, and +f == f. There is no use to it.

The rest of the operators do what you'd expect, that means -(-f) == f holds.

CastsTop

You can cast to all supported types and to bool. Example cast:

    Fraction f = 3.75;
    int i = (int)f;
    // i is now 3
    

Functions

is_supported_type()Top


    template <typename T> static constexpr bool npasson::Fraction::is_supported_type()
    
Example usage:

    if(Fraction::is_arithmetic_type<float>()) {
        // do something
    }
    
Returns true if T is on the list of supported types.

valid()Top


    bool npasson::Fraction::valid() const
    
Example usage:

    Fraction f = 3;
    f /= 0;

    if(f.valid()) {
        // does not execute, f has been divided by zero and is not valid anymore
    }
    
Returns false if the Fraction is invalid. In that case, all operations made on the Fraction are undefined behavior. This can happen in three cases:
  1. The Fraction has been subjected to an invalid operation, such as dividing by zero.
  2. The Fraction has been initialized with a std::string/char* which does not represent a number.
  3. The Fraction has been initialized via Fraction(false).

str()Top


    std::string npasson::Fraction::str()
    
Returns a decimal std::string representation of the Fraction.
Example usage:

    Fraction f = 25;

    std::string str = "I am " + f.str() + " years old!";
    

c_str()Top


    const char* npasson::Fraction::c_str()
    
Returns a decimal const char* representation of the Fraction.
Example usage:

    Fraction f = 25;

    printf("I am %s years old!", f.c_str());
    

f_str()Top


    std::string npasson::Fraction::f_str()
    
Returns a fraction std::string representation of the Fraction.
Example usage:

    Fraction f = 3.75;

    std::string str = f.str() " in fraction form is " + f.f_str();
    std::cout << str << std::endl;
    
This will output:

    3.75 in fraction form is 15/4
    

invert()Top


    npasson::Fraction npasson::Fraction::invert() const
    
Returns an inverted fraction.

invert(Fraction)Top


    static void npasson::Fraction::invert(Fraction&)
    
Inverts the given Fraction.

pow(int)Top


    npasson::Fraction npasson::Fraction::pow(int)
    
f.pow(x) returns fx.

abs(T)Top


    template <typename T> npasson::abs(T t)
    
Returns the absolute value of the type. The type needs to support operator-() and operator<(T, T).

Debugging

To enable debugging functions, you need to #define NPASSON_DEBUG before including the header.

test(T,U)Top


    template <typename T, typename U> npasson::Fraction::test(T& a, U& b)
    

Prints the complete list of operators available to the Fraction class and the results between the two types. Results are cast to double after operation to ensure displayability. This can be used to find bugs in operators.

Example usage:


    float one = 16;
    npasson::Fraction two = 3.5;
    npasson::Fraction::test(one, two);
    
This prints:

    ==========================
    |   Operator test run    |
    ==========================
    | a is      16 of type float
    | b is     3.5 of type npasson::Fraction
    | a+b is    19.5 of type npasson::Fraction
    | b+a is    19.5 of type npasson::Fraction
    | a-b is    12.5 of type npasson::Fraction
    | b-a is   -12.5 of type npasson::Fraction
    | a*b is      56 of type npasson::Fraction
    | b*a is      56 of type npasson::Fraction
    | a/b is  4.57143 of type npasson::Fraction
    | b/a is  0.21875 of type npasson::Fraction
    ==========================
    | (operators are reset before
    |  each following operation)
    --------------------------
    | a+=b is      19.5 of type float
    |   a is now   19.5
    |   b is now    3.5
    ---------=RESET=----------
    |   a is now     16
    |   b is now    3.5
    | b+=a is      19.5 of type npasson::Fraction
    |   a is now     16
    |   b is now   19.5
    ---------=RESET=----------
    |   a is now     16
    |   b is now    3.5
    | a-=b is      12.5 of type float
    |   a is now   12.5
    |   b is now    3.5
    ---------=RESET=----------
    |   a is now     16
    |   b is now    3.5
    | b-=a is     -12.5 of type npasson::Fraction
    |   a is now     16
    |   b is now  -12.5
    ---------=RESET=----------
    |   a is now     16
    |   b is now    3.5
    | a*=b is        56 of type float
    |   a is now     56
    |   b is now    3.5
    ---------=RESET=----------
    |   a is now     16
    |   b is now    3.5
    | b*=a is        56 of type npasson::Fraction
    |   a is now     16
    |   b is now     56
    ---------=RESET=----------
    |   a is now     16
    |   b is now    3.5
    | a/=b is    4.57143 of type float
    |   a is now 4.57143
    |   b is now    3.5
    ---------=RESET=----------
    |   a is now     16
    |   b is now    3.5
    | b/=a is    0.21875 of type npasson::Fraction
    |   a is now     16
    |   b is now 0.21875
    ---------=RESET=----------
    |   a is now     16
    |   b is now    3.5
    ==========================
    | Comparison
    | a is      16
    | b is     3.5
    | a<b is false
    | a>b is true
    | a<=b is false
    | a>=b is true
    | a==b is false
    | a!=b is true
    ==========================
    

Experimental compileTop


    #define NPASSON_EXPERIMENTAL_COMPILE
    
Add this line before including the header. This will include the .cpp file in the .hpp file instead of vice versa. This is ugly programming and is not recommended by me. Bugs only surfacing with this #define have low priority by default. That being said, with NPASSON_EXPERIMENTAL_COMPILE being defined, you can compile without having to add anything. No object file, no additional file argument. Just add the header and compile the file. This is good if you make frequent code changes to the Fraction library.
Syntax highlighting by highlight.js using the vs theme.