Prolog /
RationalsRational Numbers in ECLiPSeAuthor: Joachim Schimpf, 2020, under Creative Commons Share-Alike License HistoryRational numbers were first integrated into ECLiPSe as a first class data type in 1993. Together with the previously existing standard numeric types (integer and float) and the later introduced breal type (floating-point intervals, 2001) they are one of four supported numeric data types. ObjectiveRational numbers implement the corresponding mathematical domain, i.e., ratios of two integers (numerator and denominator). SyntaxRational constants are written as numerator and denominator separated by an underscore, e.g., 1_3 -30517578125_32768 0_1 ECLiPSe represents rationals in a canonical form where the greatest common divisor of numerator and denominator is 1 and the denominator is positive. The parser (or, more precisely the lexical analyzer) accepts non-canonical notation, but normalizes immediately. When printing, canonical form is always used: ?- X = 2_6. X = 1_3 ?- read(X). 3_9. X = 1_3 Discussion: At the time, the underscore-syntax was chosen because it did not conflict with other valid Prolog syntax. In the meantime, some Prolog systems have chosen to allow number notation with embedded underscores (such as 123_456_789) as a readability feature, which unfortunately conflicts with our rational-syntax. Some suggestions for alternative, non-conflicting syntax: 1_/3 1R3 0R1/3 Note that the following are undesirable, for the reason given: 1/3 conflicts with compound term /(1,3) in infix notation 1 rdiv 3 conflicts with compound term rdiv(1,3) in infix notation 1r3 conflicts with compound term r3(1) in postfix notation 1/3R requires 3-token lookahead in lexer to disambiguate, potentially conflicts with compound term /(1,3R) ComparisonsNumbers of different types are never identical and never unify, e.g., 3, 3_1, 3.0 and 3.0__3.0 are all different: ?- 3 == 3_1. % fails in the same way that 3==3.0 fails No. ?- 3 = 3_1. No Arithmetic comparison predicates must be used to their compare numeric values: ?- 3 =:= 3_1. Yes In the standard term ordering, rationals come between integers and floats: ?- compare(R, 3_1, 3). R = < ?- compare(R, 3_1, 3.0). R = > ComputationsRational arithmetic is precise. When numbers of different types occur as arguments of an arithmetic operation or comparison, the types are first made equal by converting to the more general of the two types, i.e., the rightmost one in the sequence integer → rational → float → bounded real The operation or comparison is then carried out with this type and the result is of this type as well, unless otherwise specified. There is potential loss of precision in the rational → float conversion! The system never does automatic conversions in the opposite direction. Such conversion must be programmed explicitly using the integer, rational, float and breal functions. These arithmetic operations work on rational inputs: Operation Meaning Argument type(s) Result type + E unary plus rational rational - E unary minus rational rational abs(E) absolute value rational rational sgn(E) sign value rational integer floor(E) round down rational rational (ISO:integer) ceiling(E) round up rational rational (ISO:integer) round(E) round to nearest rational rational (ISO:integer) truncate(E) round towards zero rational rational (ISO:integer) E1 + E2 addition rational x rational rational E1 - E2 subtraction rational x rational rational E1 * E2 multiplication rational x rational rational E1 / E2 division rational x rational rational E1 ^ E2 power operation rational x integer rational min(E1,E2) minimum of 2 values rational x rational rational max(E1,E2) maximum of 2 values rational x rational rational fix(E) truncate to integer rational integer integer(E) convert to integer rational integer float(E) convert to float rational float numerator(E) numerator of rational rational integer denominator(E) denominator of rational rational integer In addition, these operations on non-rational arguments yield (may yield) rational results: E1 / E2 division integer x integer see below E1 ^ E2 power operation integer x integer see below rational(E) convert to rational number rational rationalize(E) convert to rational number rational Currently, there is a global flag prefer_rationals: when set, the system uses rational arithmetic wherever possible. In particular
then yields a precise rational instead of a float result. Discussion: changing the behaviour via a global flag is undesirable. On the other hand, always using rational results can be very inefficient if the precision is later lost anyway due to conversion to floats. Both rational/1 and rationalize/1 produce a value that is exactly equal to the value of the floating-point input. The difference is that rationalize/2 tries to produce the most compact rational that still converts back into the original float, while rational/2 often produces unnecessarily large numerators and denominators but is more efficient. The naming convention is taken from LISP. ShortcomingsThe strict type separation of integers and rationals may not be desirable. An alternative would be to automatically convert integral rationals (i.e. rationals with a normalized denominator of 1) to integers. SWI-Prolog does it this way already. Integers then become a subset of the rationals rather than a disjoint data type. |