Calculator

MPFR による計算機能の強化

XML 拡張に続いて導入されたのが MPFR による任意精度計算でした。 gawk の計算は倍精度の浮動小数点演算のみで、精度の高い計算には不向きでしたが、MPFR の導入により大幅に強力な計算を行うことができます。

MPFR を分かってもらうためのデモとして、「プログラミング言語 AWK」の「6-5 普通の電卓」を McCormack や Alan Linton が拡張したスクリプトを MPFR に対応させてみました。 このスクリプトはそのまま本章の回答にもなっています。

mpfr_calc.awk

#! /usr/local/bin/xgawk -f
# calc3 - infix calculator - derived from calc3 in TAPL, chapter 6.
# by Kenny McCormack, Mon 3 Jan 2000
# modified by Alan Linton, $Date: 2000/01/06 21:37:36 $, $Revision: 1.16 $
# modified by Hirofumi Saito.

@load mpfr

BEGIN {
    MPFR_BASE = 10;
    MPFR_PRECISION = 100;
}

{
    printf( " = %s\n", eval($0));
}

# The rest is functions...
function eval(s   ,e) {
    _S_expr = s;
    gsub(/[ \t]+/, "", _S_expr);
    if (length(_S_expr) == 0) {
        return 0;
    }
    _f = 1;
    e = _expr();
    if (_f <= length(_S_expr)) {
        printf("An error occurred at %s\n", substr(_S_expr, _f));
    } else {
        return e;
    }
}

function _expr(    var,e) { # term | term [+-] term
    if (match(substr(_S_expr, _f), /^[A-Za-z_][A-Za-z0-9_]*=/)) {
        var = _advance();
        sub(/=$/, "", var);
        return _vars[var] = _expr();
    }
    e = _term();
    while (substr(_S_expr, _f, 1) ~ /[+-]/) {
        if (substr(_S_expr, _f++, 1) == "+") {
            e = mpfr_add(e, _term());
        } else {
            e = mpfr_sub(e, _term());
        }
    }
    return e;
}

function _term(    e) { # factor | factor [*/%] factor
    e = _factor();
    while (substr(_S_expr,_f,1) ~ /[*\/%]/) {
        _f++;
        if (substr(_S_expr, _f-1, 1) == "*") {
            return mpfr_mul(e, _factor());
        }
        if (substr(_S_expr,_ f-1, 1) == "/") {
            return mpfr_mul(e, _factor());
        }
        if (substr(_S_expr,_f-1,1) == "%") {
            return e % _factor();
        }
    }
    return e;
}

function _factor(   e) { # factor2 | factor2^factor
    e = _factor2();
    if (substr(_S_expr, _f, 1) != "^") {
        return e;
    }
    _f++;
    return mpfr_pow(e, _factor());
}

function _factor2(    e) { # [+-]?factor3 | !*factor2
    e = substr(_S_expr, _f);
    if (e ~ /^[\+\-\!]/) { #unary operators [+-!]
        _f++;
        if (e ~ /^\+/) {
            return +_factor3(); # only one unary + allowed
        }
        if (e ~ /^\-/) {
            return -_factor3(); # only one unary - allowed
        }
        if (e ~ /^\!/) {
            return !(_factor2() + 0); # unary ! may repeat
        }
    }
    return _factor3();
}

function _factor3(   e,fun,e2) { # number | varname | (expr) | function(...)
    e = substr(_S_expr, _f);

    #number
    if (match(e, /^([0-9]+[.]?[0-9]*|[.][0-9]+)([Ee][+-]?[0-9]+)?/)) {
        return _advance();
    }

    #function()
    if (match(e, /^([A-Za-z_][A-Za-z0-9_]+)?\(\)/)) {
        fun = _advance();
        if (fun ~ /^srand()/) {
            return srand();
        }
        if (fun ~ /^rand()/) {
            return rand();
        }
        if (fun ~ /^pi()/) {
            return mpfr_const_pi();
        }
        printf("error: unknown function %s\n", fun);
        return 0;
    }

    # (expr) | function(expr) | function(expr,expr)
    if (match(e, /^([A-Za-z_][A-Za-z0-9_]+)?\(/)) {
        fun=_advance();
        if (fun ~ /^((cos)|(exp)|(int)|(log)|(sin)|(sqrt)|(srand))?\(/) {
            e = _expr();
            e = _calcfun(fun, e);
        } else if (fun ~ /^atan2\(/) {
            e = _expr();
            if (substr(_S_expr, _f, 1) != ",") {
                printf("error: missing , at %s\n", substr(_S_expr, _f));
                return 0
            }
            _f++;
            e2 = _expr();
            e = mpfr_atan2(e, e2);
        } else {
            printf("error: unknown function %s\n", fun);
            return 0;
        }
        if (substr(_S_expr, _f++, 1) != ")") {
            printf("error: missing ) at %s\n", substr(_S_expr, _f));
            return 0;
        }
        return e;
    }

    #variable name
    if (match(e, /^[A-Za-z_][A-Za-z0-9_]*/)) {
        return _vars[_advance()];
    }

    #error
    printf("error in factor: expected number or ( at %s\n",
           substr(_S_expr, _f));
    return 0;
}

function _calcfun(fun, e) { #built-in functions of one variable
    if (fun == "(") {
        return e;
    }
    if (fun == "cos(") {
        return mpfr_cos(e);
    }
    if (fun == "exp(") {
        return mpfr_exp(e);
    }
    if (fun == "int(") {
        return int(e);
    }
    if (fun == "log(") {
        return mpfr_log(e);
    }
    if (fun == "sin(") {
        return mpfr_sin(e);
    }
    if (fun == "sqrt(") {
        return mpfr_sqrt(e);
    }
    if (fun == "srand(") {
        return srand(e);
    }
    if (fun == "exp(") {
        return mpfr_exp(e);
    }
}

function _advance(    tmp) {
    tmp = substr(_S_expr, _f, RLENGTH);
    _f += RLENGTH;
    return tmp;
}

実行結果

mpfr_calc.jpg