simple_fraction

User Guide

Introduction

This guide covers everything you need to know to use simple_fraction effectively. We'll start with the basics and progress to advanced topics.

Why Fractions?

Fractions provide exact representation of rational numbers. Unlike decimals (which approximate 1/3 as 0.333...), fractions store 1/3 as exactly 1/3.

The Classic Example

-- With decimals:
1.0/3.0 + 1.0/3.0 + 1.0/3.0  -- May not equal exactly 1.0

-- With fractions:
create third.make (1, 3)
third + third + third  -- Equals exactly 1 (3/3 reduced)

Getting Started

Installation

  1. Set the environment variable:
    # Unix/Linux/macOS
    export SIMPLE_EIFFEL=/d/prod
    
    # Windows
    set SIMPLE_EIFFEL=D:\prod
  2. Add to your ECF:
    <library name="simple_fraction" location="$SIMPLE_EIFFEL/simple_fraction/simple_fraction.ecf"/>

Your First Fraction

local
    half: SIMPLE_FRACTION
do
    create half.make (1, 2)
    print (half.out)           -- "1/2"
    print (half.to_double.out) -- "0.5"
end

Creating Fractions

From Numerator and Denominator

create half.make (1, 2)         -- 1/2
create three_quarters.make (3, 4) -- 3/4
create negative.make (-1, 2)    -- -1/2

Automatic Reduction

Fractions are automatically reduced to lowest terms:

create f.make (2, 4)   -- Becomes 1/2
create f.make (6, 9)   -- Becomes 2/3
create f.make (100, 25) -- Becomes 4
create f.make (3, -4)  -- Becomes -3/4 (sign in numerator)

From Integer

create five.make_integer (5)   -- 5 (= 5/1)
create neg.make_integer (-3)   -- -3

From Mixed Number

Mixed numbers like "2 3/4" are whole + fraction:

create f.make_mixed (2, 3, 4)   -- 2 3/4 = 11/4
create f.make_mixed (1, 1, 2)   -- 1 1/2 = 3/2
create f.make_mixed (-2, 1, 4)  -- -2 1/4 = -9/4

From String

The string parser accepts multiple formats:

create f.make_from_string ("3/4")     -- Simple fraction
create f.make_from_string ("-1/2")    -- Negative fraction
create f.make_from_string ("2 3/4")   -- Mixed number
create f.make_from_string ("5")       -- Integer
create f.make_from_string ("0.5")     -- Decimal -> 1/2
create f.make_from_string ("0.25")    -- Decimal -> 1/4

Special Values

create z.make_zero    -- 0
create o.make_one     -- 1

Arithmetic Operations

All operations are immutable - they return new fractions without modifying originals.

Basic Operations

local
    a, b, r: SIMPLE_FRACTION
do
    create a.make (1, 2)
    create b.make (1, 4)

    r := a + b    -- Addition: 1/2 + 1/4 = 3/4
    r := a - b    -- Subtraction: 1/2 - 1/4 = 1/4
    r := a * b    -- Multiplication: 1/2 * 1/4 = 1/8
    r := a / b    -- Division: 1/2 / 1/4 = 2
end

Other Operations

r := f.negate      -- Flip sign: 3/4 -> -3/4
r := f.absolute    -- Absolute value: -3/4 -> 3/4
r := f.reciprocal  -- Flip fraction: 3/4 -> 4/3
r := f.power (2)   -- Square: (2/3)^2 = 4/9
r := f.power (-1)  -- Negative power = reciprocal

Scaling

-- Multiply by integer
r := f.scale (3)        -- 1/4 * 3 = 3/4

-- Divide by integer
r := f.scale_down (2)   -- 3/4 / 2 = 3/8

Comparison

SIMPLE_FRACTION implements COMPARABLE:

if a < b then ...
if a <= b then ...
if a > b then ...
if a >= b then ...
if a.is_equal (b) then ...

Status Queries

f.is_zero           -- Is 0?
f.is_negative       -- Is less than 0?
f.is_positive       -- Is greater than 0?
f.is_integer        -- Is whole number (denominator = 1)?
f.is_proper         -- Is |numerator| < denominator? (like 3/4)
f.is_improper       -- Is |numerator| >= denominator? (like 5/4)
f.is_unit_fraction  -- Is numerator = 1? (like 1/4)

Mixed Numbers

Mixed numbers combine a whole part with a fractional part, like 2 3/4.

Creating Mixed Numbers

-- From parts
create f.make_mixed (2, 3, 4)   -- 2 3/4 = 11/4

-- From string
create f.make_from_string ("2 3/4")

-- Note: internally stored as improper fraction (11/4)

Displaying as Mixed

create f.make (11, 4)
print (f.out)              -- "11/4"
print (f.to_mixed_string)  -- "2 3/4"

Extracting Parts

create f.make (11, 4)
f.whole_part        -- 2 (INTEGER_64)
f.fractional_part   -- 3/4 (SIMPLE_FRACTION)

Proper vs Improper

create f.make (3, 4)   -- Proper: numerator < denominator
f.is_proper   -- True
f.is_improper -- False

create f.make (5, 4)   -- Improper: numerator >= denominator
f.is_proper   -- False
f.is_improper -- True

Output Formatting

Basic Output

create f.make (3, 4)
f.out              -- "3/4"
f.to_string        -- "3/4"

create f.make (5, 1)
f.out              -- "5" (integers shown without denominator)

Mixed Number Output

create f.make (11, 4)
f.to_mixed_string  -- "2 3/4"

create f.make (3, 4)
f.to_mixed_string  -- "3/4" (proper fractions unchanged)

Decimal Output

create f.make (3, 4)
f.to_decimal_string (2)  -- "0.75"
f.to_decimal_string (4)  -- "0.7500"

Numeric Conversion

f.to_double     -- Convert to DOUBLE (may lose precision)
f.to_integer    -- Convert to INTEGER_64 (truncates)

Factory Helpers

Common fractions available as factory methods:

-- Unit fractions
f.half      -- 1/2
f.third     -- 1/3
f.quarter   -- 1/4
f.zero      -- 0
f.one       -- 1

-- Multiples of common denominators
f.halves (3)      -- 3/2
f.thirds (2)      -- 2/3
f.quarters (3)    -- 3/4
f.eighths (5)     -- 5/8
f.sixteenths (7)  -- 7/16

Best Practices

1. Use Fractions for Exact Math

When exactness matters, use fractions:

-- GOOD: Exact
create third.make (1, 3)
total := third + third + third  -- Exactly 1

-- AVOID: Approximate
total := 1.0/3.0 + 1.0/3.0 + 1.0/3.0  -- Maybe 0.9999...

2. Convert to Decimal at Display Time

-- Do calculations in fractions
result := (a + b) * c / d

-- Convert only when displaying
print (result.to_decimal_string (2))

3. Use Mixed Numbers for User Display

-- Internal: improper fraction
create f.make (11, 4)

-- Display: mixed number
print (f.to_mixed_string)  -- "2 3/4"

4. Handle Edge Cases

-- Check before division
if not divisor.is_zero then
    result := amount / divisor
end

-- Check before reciprocal
if not f.is_zero then
    r := f.reciprocal
end

Next Steps