# Rational number (ℚ) support
SWI-Prolog supports rational numbers and rational arithmetic. Rudamental
support has been around for many years. As of version 8.1.22, rational
numbers have a syntax and are an atomic data type that can be checked
using rational/1. All integers are considered rational numbers. There
are two syntaxes for rational numbers
- The `compatibility` syntax writes rational numbers as e.g., `1r3`.
Both numerator
and denominator are decimal integers, the numerator can be negative.
- The `natural` syntax replaces the `r` with `/`, so we get `1/3`.
This is enabled by setting the Prolog flag `rational_syntax`
to `natural`.
Rational numbers are always in _canonical form_, which implies the
denominator is a positive integer, the numerator is any integer, the
numerator and denominator have no common divisors and if the denominator
is `1`, the result is an integer.
If the Prolog flag `prefer_rationals` is `true`, all arithmetic that has
a precise result as a rational number produces a rational number rather
than a float, _except_ pure float functions, e.g., `cos(0)` is `1.0`
rather than `1`. This rule notably affects division (`A/B`) and
exponentiation (`A^B` or `A**B`)
A is 1r3 + 5r7.
Things that are true about rational numbers
Num is 1r3,
rational(Num),
\+ integer(Num),
number(Num),
atomic(Num),
Num > 0, Num < 1,
Num @> 0, Num @< 1,
Num > 0.0, Num < 1.0,
Num @> 0.0, Num @< 1.0.
Integers are rational numbers and rational numbers with a denominator of
`1` are integers
A is 4r2,
integer(A).
## Rational numbers in the standard order of terms
Rational numbers are numbers and SWI-Prolog standard order of terms
compares numbers by value (unlike ISO where all floats are before all
integers). If a float and a rational number are equal as defined by
=:=/2, the float comes before the rational.
sort([2, 1r3, 1, 1.0, 3r2, 1.0], List).
## Tripwire to protect against huge rational numbers
Most programs run fine when using rational numbers instead of floats
while producing precise results. There are some exceptions however.
Consider the program below which sums all 1/N quotients up to some
limit.
Please run the two subsequent queries with both `max_rational_size`
enabled and disabled (see bottom). The term_size/2 predicate returns the
size of a Prolog term on the _global stack_ in _cells_ (8 bytes on a
64-bit machine).
sum(N, Sum) :-
sum(N, N, 0, Sum).
sum(I, N, S0, S) :-
I > 0,
!,
S1 is S0 + 1/I,
I2 is I-1,
sum(I2, N, S1, S).
sum(_, _, S, S).
sum(10, Sum).
sum(100 000, Sum),
term_size(Sum, Size).
## Forcing float arithmetic
If you are not interested in precise results for some division or
exponentiation, force one of the operants to float. For example rewrite
the example above as below. Note that you get the same result if you
change the start `0` to `0.0`, but that performs all division using
rational numbers and is slower.
If you want float division and the type of both operants is unknown, use
the function float/1. Only one of the arguments needs to be casted this
way.
A is X/float(Y).
sum(N, Sum) :-
sum(N, N, 0, Sum).
sum(I, N, S0, S) :-
I > 0,
!,
S1 is S0 + 1.0/I, % use float argument
I2 is I-1,
sum(I2, N, S1, S).
sum(_, _, S, S).
time(sum(100 000, Sum)).
#### Set the relevant flags
You can toggle the flags below to check there effect. As a query only
uses global marked program fragments and the program immediately above
it, setting the fragment to local effectively disables it.
:- set_prolog_flag(prefer_rationals, true).
:- set_prolog_flag(rational_syntax, natural).
:- set_prolog_flag(max_rational_size, 1000).
:- set_prolog_flag(max_rational_size_action, float).