.\" Copyright (C), 2023 Duncan Wilkie .\" You may distribute this file under the terms of the GNU Free .\" Documentation License. .TH edc 1 2023-11-15 .ds edc \fIedc\fP .ds Edc \fIedc\fP .SH NAME edc \- the exact desk calculator .SH SYNOPSIS edc [-?V] [-e scriptexpression] [-f script-file] [--expression=scriptexpression] [--file=script-file] [--help] [--usage] [--version] [file ...] .SH DESCRIPTION .PP .B Edc is a .MR dc 1 style RPN calculator program, distinct in its use of exact real arithmetic. It uses IC-Reals, a set of C routines developed at Imperial College that implement a system for performing exact (not arbitrary-precision) computations with real numbers (Edalat and Heckmann 2002). The distinction is subtle, but has significant consequences: try computing the 100th term of the sequence .EQ a(0) = 11 / 2, .EN .EQ a(1) = 61 / 11, .EN .EQ a(n) = 111 ^-^ (1130 ^-^ 3000 ^/^ a(n ^-^ 2)) ^/^ a(n ^-^ 1) .EN in Emacs calc, at precisions 5, 10, 30, 60, 100, 110, 120, 130, and 140. .EX ;; Evaluate, and then: M-x calc RET p 5 RET 'hell(100) RET ;; etc. (defmath hell (n) (let ((nm2 (/ :"11.0" :"2.0")) (nm1 (/ :"61.0" :"11.0")) (old 0)) (for ((i 3 n)) (setq old nm1) (setq nm1 (- 111 (/ (- 1130 (/ 3000 nm2)) nm1))) (setq nm2 old)) nm1)) .EE You get the results .TS box center; Cb Cb L C. Precision Estimate 5 100. 10 100. 30 100. 60 99.(9^57)7 100 100.0000000... 120 100.(0^31)3... 130 5.(9^7)83... 140 5.(9^7)85... .TE This sequence provably converges to 6. And that should be scary; who would compute this to 130 digits of precision if the value for 60 were so near to a round number? Who would trust the result for 130 digits; how could you tell it was more correct? Maybe what caused this problem is worse with more digits. Not only do you need to worry about the truncation error of the sequence, but also the accumulated rounding error! .PP Exact real arithmetic avoids this problem via lazy precision. Numbers are viewed as streams of (signed) digits encoding chains of descending subsets, and when a computation returns, the result truly must be within the indicated subset. .PP The .B edc incantation .EX [0:1 0:2 111 1130 3000 0;2 / - 0;1 / - 0;1 r]0:s 11/2 61/11 0;sx0;sx0;sx0;sx0;sx .EE gives approximately 5.75; computing further terms yields a sequence that seems to monotonically approach 6. If you repeat 0;sx 100 times (to compute the 100th term), the result is 6 to within 2 in 10^8. .PP .B Edc has very similar command syntax to its predecessor. Some exceptions are: .TP .PP Comparisons are ordered like a reasonable person would expect, i.e. '1 2 <' corresponds to infix '1 < 2', just as '1 2 -' corresponds to '1 - 2'. .TP .PP There is no 'X' command; 'Z', in addition to producing the number of digits of an integer and length of a string, simply produces the number of currently-computed digits of a real value. .TP .PP There is no '!' command. Enjoy the absence of shell-injection. .TP .PP There are no decimal points, save in output: real-valued input is provided through rational numbers. Examples: '1/2', '2/1', '123412341234/987098709870987'. Non-examples: '1 / 2', '12.7', '1/'. Irrational and transcendentals must be accessed by commands. This choice emphasizes the computable, non-floating-point nature of the tool. (And, the real reason, was easier to implement.) .TP .PP Comparisons take an extra integer argument, last on the stack: a precision. They execute their macros only if the comparison is provably true for the passed values computed to within the given precision. As a special case, numbers are considered equal if they are not provably not equal. (Comparisons of integers are, by contrast, computable; they have their usual meaning, and the precision is ignored.) .TP .PP The 'P' command instead pushes pi onto the stack. .TP .PP The 'n' command is renamed to 'm'. .TP .PP There are additional commands for common constants and special functions. .PD .SH OPTIONS .B Edc may be invoked with the following command-line options: .TP .B -V .TP .B --version Print out the version of .B edc that is being run and a copyright notice, then exit. .TP .B -h .TP .B --help Print a usage message briefly summarizing these command-line options and the bug-reporting address, then exit. .TP .B -e \fIscript\fP .TP .BI --expression= script Add the commands in .I script to the set of commands to be run while processing the input. .TP .B -f \fIscript-file\fP .TP .BI --file= script-file Add the commands contained in the file .I script-file to the set of commands to be run while processing the input. .PP If any command-line parameters remain after processing the above, these parameters are interpreted as the names of input files to be processed. A file name of .B - refers to the standard input stream. The standard input will be processed if no script files or expressions are specified. .PD .SH Printing Commands .TP .B p Prints the value on the top of the stack, without altering the stack. A newline is printed after the value. .TP .B n Prints the value on the top of the stack, popping it off, and does not print a newline after. .TP .B f Prints the entire contents of the stack .ig and the contents of all of the registers, .. without altering anything. This is a good command to use if you are lost or want to figure out what the effect of some command has been. .PD .SH Arithmetic .TP .B + Pops two values off the stack, adds them, and pushes the result. .TP .B - Pops two values, subtracts the first one popped from the second one popped, and pushes the result. .TP .B * Pops two values, multiplies them, and pushes the result. .TP .B / Pops two values, divides the second one popped from the first one popped, and pushes the result. Performs a C-style integer division if both arguments are integers. .TP .B % Pops two values, computes the remainder of the division that the .B / command would do, and pushes that. The value computed is the same as that computed by the sequence \fBSd dld/ Ld*-\fP by definition. .TP .B ~ Pops two values, divides the second one popped from the first one popped. The quotient is pushed first, and the remainder is pushed next. The value is the same as \fBSdSn lnld/ LnLd%\fP by definition. .TP .B ^ Pops two values and exponentiates, using the first value popped as the exponent and the second popped as the base. .TP .B | Pops three values and computes a modular exponentiation. The first value popped is used as the reduction modulus; this value must be a non-zero number, and should be an integer. The second popped is used as the exponent; this value must be a non-negative number, and any fractional part of this exponent will be ignored. The third value popped is the base which gets exponentiated, which should be an integer. For small integers this is like the sequence \fBSm^Lm%\fP, but, unlike \fB^\fP, this command will work with arbitrarily large exponents. .TP .B v Pops one value, computes its square root, and pushes that. .PP Real values are forced on printing; the .B k command sets the number of digits after the decimal point to print. The default display precision value is 10. .SH Stack Control .TP .B c Clears the stack, rendering it empty. .TP .B d Duplicates the value on the top of the stack, pushing another copy of it. Thus, ``4d*p'' computes 4 squared and prints it. .TP .B r Reverses the order of (swaps) the top two values on the stack. .TP .B R Pops the top-of-stack as an integer .IR n . Cyclically rotates the top .I n items on the updated stack. If .I n is positive, then the rotation direction will make the topmost element the second-from top; if .I n is negative, then the rotation will make the topmost element the .IR n -th element from the top. If the stack depth is less than .IR n , then the entire stack is rotated (in the appropriate direction), without any error being reported. .SH Registers .PP .B Edc provides 256 memory registers, each named by a single character. You can store a number or a string in a register and retrieve it later. .TP .BI s r Pop the value off the top of the stack and store it into register .IR r . .TP .BI l r Copy the value in register .I r and push it onto the stack. The value 0 is retrieved if the register is uninitialized. This does not alter the contents of .IR r . .PP Each register also contains its own stack. The current register value is the top of the register's stack. .TP .BI S r Pop the value off the top of the (main) stack and push it onto the stack of register .IR r . The previous value of the register becomes inaccessible. .TP .BI L r Pop the value off the top of register .IR r 's stack and push it onto the main stack. The previous value in register .IR r 's stack, if any, is now accessible via the .BI l r command. .SH Parameters .PP .B Edc has three parameters that control its operation: the display precision, the input radix, and the output radix. The display precision specifies the number of real digits after the decimal point to print. The input radix controls the interpretation of numbers typed in; all numbers typed in use this radix. The output radix is used for printing integers (all reals are output in decimal, as IC-Reals doesn't expose anything that makes that easy). Bases greater than 10 use as extra digits capital letters A through F; just as with .MR dc 1 the bases 2 through 16 are supported. .PP The input and output radices are separate parameters; you can make them unequal, which can be useful or confusing. Each must be between 2 and 16 inclusive. The output must be nonnegative. .TP .B i Pops the value off the top of the stack and uses it to set the input radix. .TP .B o Pops the value off the top of the stack and uses it to set the output radix. .TP .B k Pops the value off the top of the stack and uses it to set the precision. .TP .B I Pushes the current input radix on the stack. .TP .B O Pushes the current output radix on the stack. .TP .B K Pushes the current precision on the stack. .SH Strings .PP .B Edc has a limited ability to operate on strings as well as on numbers; the only things you can do with strings are print them and execute them as macros (which means that the contents of the string are processed as .B edc commands). All registers and the stack can hold strings, and .B edc always knows whether any given object is a string or a number. Some commands such as arithmetic operations demand numbers as arguments and print errors if given strings. Other commands can accept either a number or a string; for example, the .B p command can accept either and prints the object according to its type. .TP .BI [ characters ] Makes a string containing .I characters (contained between balanced .B [ and .B ] characters), and pushes it on the stack. For example, .B [foo]P prints the characters .B foo (with no newline). .TP .B a The top-of-stack is popped. If it was an integer, then the low-order byte of this number is converted into a string and pushed onto the stack. If it was a string, the first character of that string is pushed back. If it is real, an error is signalled, as reals have infinite digits. .TP .B x Pops a value off the stack and executes it as a macro. Normally it should be a string; if it is a number, it is simply pushed back onto the stack. For example, .B [1p]x executes the macro .B 1p which pushes .B 1 on the stack and prints .B 1 on a separate line. .PP Macros are most often stored in registers; .B [1p]sa stores a macro to print .B 1 into register .BR a , and .B lax invokes this macro. .TP .BI > r Pops three values off the stack and compares the lowest two, assuming they are numbers, within the precision indicated by the topmost. The contents of register .I r are executed as a macro if the lowest is provably greater within the precision. The precision is ignored if both are integers. Thus, .B 2 1 0>a will invoke register .BR a 's contents and .B 1 2 0>a will not. .TP .BI !> r Similar, but invokes the macro if the lowest is provably not greater (less than or equal to) the second lowest. .TP .BI < r Similar, but invokes the macro if the lowest is less than the second lowest. .TP .BI !< r Similar, but invokes the macro if the lowest is provably not less (greater than or equal to) the second lowest. .TP .BI = r Invokes invokes the macro if the two numbers popped are not provably not equal, i.e. if the intervals computed at the specified precision overlap. .TP .BI != r Similar, but invokes the macro if the two numbers popped are provably not equal, i.e. if the intervals computed at the specified precision do not overlap. .TP .B ? Reads a line from the terminal and executes it. This command allows a macro to request input from the user. .TP .B q exits from a macro and also from the macro which invoked it. If called from the top level, or from a macro which was called directly from the top level, the .B q command will cause .B edc to exit. .TP .B Q Pops a value off the stack and uses it as a count of levels of macro execution to be exited. Thus, .B 3Q exits three levels. The .B Q command will never cause .B edc to exit. .SH Status Inquiry .TP .B Z Pops a value off the stack, calculates the number of decimal digits it has (or number of characters, if it is a string) and pushes that number. The digit count for a number does .I not include any leading zeros, even if those appear to the right of the radix point. If the value is a real number, it pushes the number of digits computed so far. .TP .B z Pushes the current stack depth: the number of objects on the stack before the execution of the .B z command. .SH Edc Extensions .TP .B N Pops a value off the stack, pushing its negation. .TP .B M Pops a value off the stack, pushing its magnitude (absolute value). .TP .B e Pops a value off the stack, and pushes the exponential function of the result. .TP .B g Pops a value off the stack, and pushes the natural logarithm of the result. .TP .B t Pops a value off the stack. Interprets the next character (s(in), c(os), t(an), S(ec), C(sc), (co)T) as a trigonometric function, pushing its result. .TP .B h Pops a value off the stack. Interprets the next character (s(inh), c(osh), t(anh), S(ech), C(sch), (co)T(h)) as a hyperbolic trigonometric function, pushing its result. .TP .B n Pops a value off the stack. Interprets the next two characters as a trigonometric or hyperbolic trigonometric function, pushing the result of the corresponding iNverse. .TP .B P Pushes pi. .TP .B X Pushes Euler's eXponential constant. .SH Miscellaneous .TP .B # Will interpret the rest of the line as a comment. .TP .BI : r Will pop the top two values off of the stack. The old second-to-top value will be stored in the array .IR r , indexed by the old top-of-stack value. .TP .BI ; r Pops the top-of-stack and uses it as an index into the array .IR r . The selected value is then pushed onto the stack. .P Note that each stacked instance of a register has its own array associated with it. Thus \fB1 0:a 0Sa 2 0:a La 0;ap\fP will print 1, because the 2 was stored in an instance of 0:a that was later popped. .SH Errors Quoting the libc manual, ``the experienced user will know what is wrong.'' For the inexperienced: .TP .BI ? c Unrecognized command .IR c . .TP .B !- Stack underflow. .TP .BI ! r - Register .IR r stack underflow. .TP .B !+ Stack overflow. .TP .BI ! r + Register .IR r stack overflow. .TP .B !? Unsupported value. Errors that cause the program to prematurely exit are bugs. .SH BUGS Email bug reports to .B duncannwilkie@gmail.com .SH AUTHOR Duncan Wilkie