题解 UVA12413 【Big Decimal Calculator】

· · 题解

2024/10/10:代码重构,题解重写

你需要实现一个高精度小数类,同时支持 15 种运算,包括加减乘除,\exp\text{pow}\ln\text{sqrt},三角函数和反三角函数等。

补充条件:在新的代码下,根据 assert 返回的结果,加减法的结果并不会非常靠近 0,所以对答案在精度范围内等于 0 的值直接输出 0 而忽略符号是正确的。

在之前的代码版本,我们实现了以十进制压位定点小数为基础的高精度小数类。这份代码在最终数据上跑了整整 210ms,这实在是太慢了,那么不妨考虑利用古法手搓一个性能稍微好一些的高精度小数类。

不妨以 Python 实现的大整数类为基础实现定点小数类。我们首先观察 Python 实现的大整数类。简单来说,一个大整数包含两个元素:

Python 实现的大整数将 30 个 bit 压入一个 int 中,也就是 data[] 储存的数字是 2^{30} 进制的。这种储存方式对十进制操作可能有些不友好,但是可以极大程度加快后续计算的速度。这一点可以在若干个方面体现:

不妨将这个思路运用在小数处理上。我们最终需要保留 50 位,假设为了精度还需要添加 k 位,那么储存小数部分所需要的数字个数就是 D = \lceil \log_2(10) * \frac{50 + k}{30} \rceil。由于本题答案的整数部分不超过 10 位,那么 k 需要在 10 的基础上进行微调,以保证计算过程中的误差值堆积不会超出 \epsilon = 0.5 \times 10^{-50}。在实现上,可以选择 k = 13,对应 D = 7 时的最大可能值。

根据同样的思路,对于一个小数,我们只需要将其乘上 2^{30 D} 的结果储存在一个 int 数组即可。同理定义 size。在这个基础上,考虑分别处理需要实现的功能。

I/O

我们需要将一个十进制小数字符串转换为设计好的小数类,同时也需要将小数类转换为十进制字符串。

为了加速转换流程,考虑将若干个十进制数位放在一起考虑。由于 2^{30} > 10^9,不妨选择将 K = 9 个十进制数位同时处理。

考虑 Input 部分。我们首先需要初始化数组,那么可以先获取整数部分的长度 l,随后推断出最大的可能长度:

L = D + \lceil \log_2(10) \times \frac{l}{30} \rceil

在新建数组并初始化之后,将数组分为整数部分和小数部分,随后分别计算。

整数部分

从前往后 K 个为一组读取数组,直到整数部分被完全读取。假设当前读取了 u 个字符,对应的数字为 x,那么在数组的整数部分上模拟 I \leftarrow I \times 10^u + x。这部分需要预处理 10 的幂次。

小数部分

我们将小数部分的字符串从前往后 K 个为一组进行分割,分别转换为十进制数字并储存在一个序列中。在字符串长度不是 K 的倍数的时候,需要在末尾补 0

随后从高到低确认小数部分的 D 个数字,每次在这个序列上模拟乘以 2^{30} 的操作,那么最高位的进位对应的就是当前小数部分的数字。

最后可以考虑四舍五入。如果进一步计算(也就是序列乘以 2^{30} 后取进位)的答案不小于 2^{29}(对应序列最高位置上的数字不小于 {0.5 \times 10^K}),那么需要从数组的低位开始不断加 1,直到不产生进位为止。具体请参考代码。

随后是 Output 部分,不妨假设题目要求计算 p 位小数。我们首先将数组进行拷贝,在新数组上,分别对整数部分和小数部分操作即可。

小数部分

通过和 Input 类似的方案,每次对小数部分的数组乘上 10^K,并取最高位的进位,我们就从高到低获得了小数部分的十进制表示。我们在此处获取 p+1 位。

随后考虑小数部分的最低位,如果其不小于 5,则需要进行四舍五入,此时在字符串上不断模拟加法,直到不产生进位或者进位到整数部分。如果进位到整数部分,则需要特殊记录,并在整数部分的开始加到个位上。

整数部分

首先获得整数部分长度 l,可以预估出整数部分字符串的长度

L' = \lceil \log_{10}(2) \times 30 l \rceil

在新建数组后不断对整数部分除以 10^K 并获得余数,就可以从低到高获得整数部分。

最后将两部分拼接并输出即可。

基础运算

对于加减法,考虑到储存方式的特殊性,我们可以首先实现两个函数,分别计算小数绝对值的加减法(这对应 CPython 里的 x_addx_sub 函数),然后在调用时进行讨论即可。

对于乘法,考虑到数组长度并不大,故直接使用暴力乘法即可。当我们计算结果的每一位时,考虑到累加乘积的时候可能会超出范围,需要使用两个辅助变量,分别代表当前计算结果和进位,在当前结果过大的时候提前计算进位。

对于除法,首先通过除数和被除数的长度估算出答案的最大可能长度,然后将除数的最高位对齐到被除数最高位的上一位,每次除以 2 后进行比较即可从高到低获得答案的二进制位。

我们可以另外实现一些乘以/除以整数的功能,以及 <<=>>= 重载(对应为移动数组)。

数学运算

剩下的就是一些数学运算了。

exp

使用如下公式计算即可。本题保证答案不会超过 10^{10},所以传入的参数比较小,收敛比较迅速。

e^x = \sum_{k=0}^{+\infty} \dfrac{x^k}{k!}

ln

首先,如果参数 x 小于 1,则计算 - \ln \frac{1}{x}

否则,可以获取 x 的整数部分长度,然后通过 >>= 运算符快速将其降低到 2^{30} 范围内。由于我们可以快速将 x 除以 2,那么不断进行除法直到 x < 1.5 为止(这个数字需要保证其本身和倒数都比较靠近 1)。在整个过程中,计算除以 2 的次数,并乘以 \ln(2) 作为答案的第一部分。

剩余的就是计算 \ln x 本身作为第二部分。由于此时 0.75 \leq x \leq 1.5,使用下面的公式即可快速收敛:

\ln(\dfrac{1+t}{1-t}) = 2 \sum_{k=1}^{+\infty} \dfrac{t^{2k - 1}}{2k - 1}

其中

x = \frac{1+t}{1-t} \rightarrow t = \dfrac{1-x}{1+x} \in [-\dfrac{1}{5}, \dfrac{1}{7}]

sqrt

使用牛顿迭代法即可,令 a \leftarrow x(也可以根据 x 的数量级计算根号的近似值),然后每次计算如下公式,直到答案不发生变化为止:

a \leftarrow \dfrac{a^2 + x} {2 a}

这里有一个需要注意的点:最终 a 的值可能会在某两个数字之间来回变化,此时可以通过将前后数字的差值与 \epsilon 比较的方案避免。‘

pow

\operatorname{pow}(a, x) = \exp(\ln a \times b)

sin

首先处理正负性,然后将参数对 2 \pi 取模后用如下公式计算:

\sin x = \sum_{k=0}^{+ \infty} (-1)^k \dfrac{x^{2k + 1}}{(2k + 1)!}

cos

同理用如下方式计算:

\cos x = \sum_{k=0}^{+ \infty} (-1)^k \dfrac{x^{2k}}{(2k)!}

tan

\tan x = \dfrac{\sin x}{\cos x}

asin

由于 asin 函数对应的级数收敛的不是很快,故可以转换为 atan 计算:

\arcsin x = \arctan \dfrac{x}{\sqrt{1 - x^2}}

acos

使用下面的公式转换为 \arcsin,进一步转换为 \arctan

\arccos x = \dfrac \pi 2 - \arcsin x

atan

atan 函数存在级数:

\arctan x = \sum_{k=0}^{+ \infty} (-1)^k \dfrac{x^{2k + 1}}{2k + 1}

这个级数在 x 绝对值较大时表现并不优秀,故需要根据 \tan 的半角公式化简:

\arctan x = 2 \arctan \dfrac{\sqrt{1 + x^2} - 1}{x}

通过上面的公式不断迭代,直到 x 的绝对值足够小即可。

atan2

根据参数对应的点在平面坐标系下的位置判定即可。具体的公式可以参考其他题解或者网络资源。

在实现以上细节,并且用字符串初始化一些常量之后,我们就可以完成这道题目了。以下的代码在官方的数据中获得了 0ms 的好成绩,足以说明了以二进制为基础的高精度数字类的确可以在复杂计算中提供速度优势。

另外,你可以在 uDebug 中进行调试。这道题目中有一个较为优秀的自测数据,但是有 65 个点存在若干问题(比如答案超出了 10^{10} 的范围,或者加减法的结果过于靠近 0 导致符号位错乱)。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

namespace big {
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;

struct BigDecimal {
private:
    int _size;
    int *_digits;
public:
    static const int IDLE_DIGITS = 13;
    static const int MAX_BITS = 30;
    static const int MAX_VALUE = (1 << MAX_BITS);
    static const int MAX_MASK = (1 << MAX_BITS) - 1;
    static const int conv_width = floor(log10(MAX_VALUE - 1));
    static const i64 conv_pow = pow(10, conv_width);
    static const int DIG = 50;
    static const int DLEN = ceil((DIG + IDLE_DIGITS) * log2(10) / MAX_BITS);
    int long_size(i64 x) {
        int res = 1; x = abs(x);
        while (x >>= MAX_BITS)
            ++ res;
        return res;
    }
    void load_long(i64 x) {
        if (x == 0)
            _size = 0, _digits = nullptr;
        else {
            _size = long_size(x) + DLEN;
            _digits = new int[_size];
            memset(_digits, 0, sizeof(int) * _size);
            if (x < 0) _size = -_size;
            int ptr = DLEN; x = abs(x);
            while (x) {
                _digits[ptr ++] = x & MAX_MASK;
                x >>= MAX_BITS;
            }
        }
    }
    BigDecimal(): _size(0), _digits(nullptr) {}
    BigDecimal(int x) { load_long(x); }
    BigDecimal(i64 x)  { load_long(x); }
    BigDecimal(const char *str) {
        bool flg = false;
        if (*str == '-')
            flg = true, ++ str;
        const char *int_part = str;
        while (*int_part != '\0' && *int_part != '.')
            ++ int_part;
        int int_len = int_part - str;
        _size = DLEN + ceil(int_len * log2(10) / MAX_BITS);
        _digits = new int[_size];
        memset(_digits, 0, sizeof(int) * _size);

        static i64 _pow10[conv_width + 1] = {0};
        if (_pow10[0] == 0) {
            _pow10[0] = 1;
            for (int i = 1; i <= conv_width; i ++)
                _pow10[i] = _pow10[i - 1] * 10;
        }

        int k, cur_width = 0, top_pos = DLEN;
        i64 cur_num = 0;
        while (str < int_part) {
            k = (*str) ^ 48;
            ++ cur_width;
            cur_num = cur_num * 10 + k;
            if ((++ str) == int_part || cur_width == conv_width) {
                for (int i = DLEN; i < top_pos; i ++) {
                    cur_num += _pow10[cur_width] * _digits[i];
                    _digits[i] = cur_num & MAX_MASK;
                    cur_num >>= MAX_BITS;
                }
                while (cur_num) {
                    _digits[top_pos ++] = cur_num & MAX_MASK;
                    cur_num >>= MAX_BITS;
                }
                cur_width = 0;
            }
        }

        if (*int_part == '.') {
            const char *dec_part = ++ int_part;
            const char *end = dec_part;
            while (*end != '\0')
                ++ end;
            if (dec_part != end) {
                int dec_len = ceil((double)(end - dec_part) / conv_width);
                int *assist = new int[dec_len];
                memset(assist, 0, sizeof(int) * dec_len);

                for (int i = 0; i < dec_len; i ++) {
                    for (int j = conv_width - 1; j >= 0; j --) {
                        if (*dec_part == '\0')
                            break;
                        assist[i] += _pow10[j] * ((*dec_part++) ^ 48);
                    }
                }

                for (int i = DLEN - 1; i >= 0; i --) {
                    i64 carry = 0;
                    for (int j = dec_len - 1; j >= 0; j --) {
                        carry += (i64)MAX_VALUE * assist[j];
                        assist[j] = carry % conv_pow;
                        carry /= conv_pow;
                    }
                    _digits[i] = carry;
                }

                if (assist[0] >= conv_pow / 2) {
                    for (int i = 0; i < _size; i ++) {
                        if (++ _digits[i] >= MAX_VALUE)
                            _digits[i] = 0;
                        else
                            break;
                    }
                }

                delete[] assist;
            }
        }
        if (flg)
            _size = -_size;
        normalize();
    }
    ~BigDecimal() {
        delete[] _digits;
    }
    void copy_from_other(const BigDecimal &bd) {
        _size = bd._size;
        if (_digits != nullptr)
            delete[] _digits, _digits = nullptr;
        if (_size != 0) {
            _digits = new int[abs(_size)];
            memcpy(_digits, bd._digits, sizeof(int) * abs(_size));
        }
    }
    BigDecimal(const BigDecimal &bd): _digits(nullptr) {
        copy_from_other(bd);
    }
    BigDecimal &operator=(const BigDecimal &bd) {
        if (this != &bd)
            copy_from_other(bd);
        return *this;
    }
    void swap(BigDecimal &bd) {
        std::swap(_size, bd._size);
        std::swap(_digits, bd._digits);
    }
    static BigDecimal epsilon() {
        BigDecimal res;
        res._size = 1;
        res._digits = new int[1];
        res._digits[0] = 1;
        return res;
    }
    int size() const { return abs(_size); }
    int int_size() const { return std::max(abs(_size) - DLEN, 0); }
    bool is_zero() const { return _size == 0; }
    bool is_nega() const { return _size < 0; }
    bool is_posi() const { return _size > 0; }
    void negate() { _size = -_size; }
    void normalize() {
        int i = size();
        while (i != 0 && _digits[i - 1] == 0)
            -- i;
        _size = (_size >= 0 ? i : -i);
    }
    void align(int new_size) {
        if (_size == 0)
            return;
        if (new_size <= 0) {
            _size = 0;
            delete[] _digits;
            _digits = nullptr;
            return;
        }
        int *temp = new int[new_size];
        memset(temp, 0, sizeof(int) * new_size);
        int valid = std::min(size(), new_size);
        memcpy(temp + std::max(0, new_size - valid)
            , _digits + std::max(0, size() - valid)
            , sizeof(int) * valid);
        if (is_nega())
            new_size = -new_size;
        _size = new_size;
        if (_digits != nullptr)
            delete[] _digits;
        _digits = temp;
    }
    void print(int MAX_DIG = DIG) {
        if (is_nega())
            putchar('-');
        int work_len = std::max((int)DLEN, size());
        int *assist = new int[work_len + 1];
        memset(assist, 0, sizeof(int) * (work_len + 1));
        if (size())
            memcpy(assist, _digits, sizeof(int) * size());

        char *dec_result = new char[MAX_DIG + 1];
        for (int i = 0; i <= MAX_DIG; i += conv_width) {
            i64 carry = 0;
            for (int j = 0; j < DLEN; j ++) {
                carry += conv_pow * assist[j];
                assist[j] = carry & MAX_MASK;
                carry >>= MAX_BITS;
            }
            for (int j = 0; j < conv_width; j ++) {
                int pos = i + conv_width - 1 - j;
                if (pos <= MAX_DIG)
                    dec_result[pos] = ('0' + carry % 10);
                carry /= 10;
            }
        }

        if (dec_result[MAX_DIG] >= '5') {
            bool carry = true;
            for (int i = MAX_DIG - 1; i >= 0 && carry; i --) {
                if (dec_result[i] == '9')
                    dec_result[i] = '0';
                else
                    ++ dec_result[i], carry = false;
            }
            if (carry) {
                for (int i = DLEN; i <= work_len; i ++) {
                    if (++ assist[i] >= MAX_VALUE)
                        assist[i] = 0;
                    else
                        break;
                }
            }
        }

        int top_pos = work_len;
        while (top_pos >= DLEN && assist[top_pos] == 0)
            -- top_pos;
        if (top_pos == DLEN - 1)
            putchar('0');
        else {
            int estimate_len = ceil((top_pos - DLEN + 1) * log10(MAX_VALUE));
            estimate_len = (estimate_len + conv_width - 1) / conv_width * conv_width;
            char *int_result = new char[estimate_len];
            for (int i = 0; i < estimate_len; i += conv_width) {
                i64 carry = 0;
                for (int j = top_pos; j >= (int)DLEN; j --) {
                    carry = (carry << MAX_BITS) ^ assist[j];
                    assist[j] = carry / conv_pow;
                    carry = carry % conv_pow;
                }
                while (top_pos >= DLEN && assist[top_pos] == 0)
                    -- top_pos;
                for (int j = 0; j < conv_width; j ++)
                    int_result[i + j] = ('0' + carry % 10), carry /= 10;
            }
            bool printed = false;
            for (int i = estimate_len - 1; i >= 0; i --)
                if (printed |= int_result[i] != '0')
                    putchar(int_result[i]);
            delete[] int_result;
        }
        putchar('.');
        for (int i = 0; i < MAX_DIG; i ++)
            putchar(dec_result[i]);
        delete[] dec_result;
    }
    friend int compare(const BigDecimal &a, const BigDecimal &b) {
        if (a._size != b._size)
            return a._size - b._size;
        for (int i = a.size() - 1; i >= 0; i --) if (a._digits[i] != b._digits[i])
            return a._size > 0 ? a._digits[i] - b._digits[i] : b._digits[i] - a._digits[i];
        return 0;
    }
    friend bool operator<(const BigDecimal &a, const BigDecimal &b) {
        return compare(a, b) < 0;
    }
    friend bool operator>(const BigDecimal &a, const BigDecimal &b) {
        return compare(a, b) > 0;
    }
    friend bool operator<=(const BigDecimal &a, const BigDecimal &b) {
        return compare(a, b) <= 0;
    }
    friend bool operator>=(const BigDecimal &a, const BigDecimal &b) {
        return compare(a, b) >= 0;
    }
    friend bool operator==(const BigDecimal &a, const BigDecimal &b) {
        return compare(a, b) == 0;
    }
    friend bool operator!=(const BigDecimal &a, const BigDecimal &b) {
        return compare(a, b) != 0;
    }

    void mul_num(i64 x) {
        if (is_zero())
            return;
        if (x < 0) x = -x, negate();
        int l = size();
        (is_nega() ? _size -= long_size(x) : _size += long_size(x));
        int *new_num = new int[size()];
        u64 carry = 0, low = x & MAX_MASK, high = x >> MAX_BITS;
        int i = 0;
        for (; i < l; i ++) {
            carry += low * _digits[i];
            new_num[i] = carry & MAX_MASK;
            carry >>= MAX_BITS;
            carry += high * _digits[i];
        }
        while (carry) {
            new_num[i ++] = carry & MAX_MASK;
            carry >>= MAX_BITS;
        }
        for (; i < size(); i ++)
            new_num[i] = 0;
        delete[] _digits;
        _digits = new_num;
        normalize();
    }
    friend BigDecimal operator*(const BigDecimal &a, const i64 x) {
        BigDecimal res = a;
        res.mul_num(x);
        return res;
    }
    friend BigDecimal operator*(const BigDecimal &a, const int x) {
        BigDecimal res = a;
        res.mul_num(x);
        return res;
    }
    void div_num(i64 x) {
        if (x < 0) x = -x, negate();
        i64 carry = 0;
        for (int i = size() - 1; i >= 0; i --) {
            carry = (carry << MAX_BITS) ^ _digits[i];
            _digits[i] = carry / x;
            carry %= x;
        }
        normalize();
    }
    friend BigDecimal operator/(const BigDecimal &a, const i64 x) {
        BigDecimal res = a;
        res.div_num(x);
        return res;
    }
    friend BigDecimal operator/(const BigDecimal &a, const int x) {
        BigDecimal res = a;
        res.div_num(x);
        return res;
    }
    friend BigDecimal &operator<<=(BigDecimal &a, const int x) {
        a.align(a.size() + x);
        return a;
    }
    friend BigDecimal &operator>>=(BigDecimal &a, const int x) {
        a.align(a.size() - x);
        return a;
    }

    friend BigDecimal num_add(const BigDecimal &a, const BigDecimal &b) {
        if (a.size() < b.size())
            return num_add(b, a);
        BigDecimal res(0);
        res._size = a.size() + 1;
        res._digits = new int[res._size];
        int i = 0, carry = 0;
        for (; i < b.size(); i ++) {
            carry = carry + a._digits[i] + b._digits[i];
            res._digits[i] = carry & MAX_MASK;
            carry >>= MAX_BITS;
        }
        for (; i < a.size(); i ++) {
            carry = carry + a._digits[i];
            res._digits[i] = carry & MAX_MASK;
            carry >>= MAX_BITS;
        }
        res._digits[i] = carry;
        res.normalize();
        return res;
    }
    friend BigDecimal num_sub_basic(const BigDecimal &a, const BigDecimal &b) {
        BigDecimal res(0);
        res._size = a.size();
        res._digits = new int[res._size];
        u32 borrow = 0;
        int i = 0;
        for (; i < b.size(); i ++) {
            borrow = a._digits[i] - borrow - b._digits[i];
            res._digits[i] = borrow & MAX_MASK;
            borrow >>= MAX_BITS;
            borrow &= 1;
        }
        for (; i < a.size(); i ++) {
            borrow = a._digits[i] - borrow;
            res._digits[i] = borrow & MAX_MASK;
            borrow >>= MAX_BITS;
            borrow &= 1;
        }
        res.normalize();
        return res;
    }
    friend BigDecimal num_sub(const BigDecimal &a, const BigDecimal &b) {
        if (a.size() > b.size())
            return num_sub_basic(a, b);
        if (a.size() < b.size()) {
            BigDecimal res = num_sub_basic(b, a);
            res.negate();
            return res;
        }
        for (int i = a.size() - 1; i >= 0; i --) if (a._digits[i] != b._digits[i]) {
            if (a._digits[i] < b._digits[i]) {
                BigDecimal res = num_sub_basic(b, a);
                res.negate();
                return res;
            }
            else
                return num_sub_basic(a, b);
        }
        return BigDecimal();
    }
    friend BigDecimal operator-(const BigDecimal &a) {
        BigDecimal res = a;
        res.negate();
        return res;
    }
    friend BigDecimal operator+(const BigDecimal &a, const BigDecimal &b) {
        if (a.is_zero())
            return b;
        if (b.is_zero())
            return a;
        if (a.is_nega()) {
            if (b.is_nega()) {
                BigDecimal res = num_add(a, b);
                res.negate();
                return res;
            }
            return num_sub(b, a);
        }
        if (b.is_nega())
            return num_sub(a, b);
        return num_add(a, b);
    }
    friend BigDecimal operator-(const BigDecimal &a, const BigDecimal &b) {
        if (a.is_zero())
            return - b;
        if (b.is_zero())
            return a;
        if (a.is_nega()) {
            BigDecimal res = (b.is_nega()? num_sub(a, b) : num_add(a, b));
            res.negate();
            return res;
        }
        if (b.is_nega())
            return num_add(a, b);
        return num_sub(a, b);
    }
    friend BigDecimal operator*(const BigDecimal &a, const BigDecimal &b) {
        if (a.size() == 0 || b.size() == 0)
            return BigDecimal();
        BigDecimal res(0);
        res._size = std::max(0, a.size() + b.size() - a.DLEN) + 1;
        res._digits = new int[res._size];
        i64 current = 0, carry = 0;
        for (int tot = - a.DLEN; tot < res._size; tot ++) {
            for (int i = std::max(tot + a.DLEN - b.size() + 1, 0); i < a.size() && i <= tot + a.DLEN; i ++) {
                current += (i64)a._digits[i] * b._digits[tot + a.DLEN - i];
                if (current >> 60) {
                    carry += current >> MAX_BITS;
                    current &= MAX_MASK;
                }
            }
            carry += current >> MAX_BITS;
            current &= MAX_MASK;
            if (tot >= 0)
                res._digits[tot] = current;
            current = carry;
            carry = 0;
        }
        if (a.is_nega() ^ b.is_nega())
            res.negate();
        res.normalize();
        return res;
    }
    void div2() {
        int carry = 0;
        for (int i = size() - 1; i >= 0; i --) {
            carry = (carry << MAX_BITS) ^ _digits[i];
            _digits[i] = carry >> 1;
            carry &= 1;
        }
        normalize();
    }
    friend BigDecimal operator/(const BigDecimal &a, const BigDecimal &b) {
        if (a.is_zero())
            return BigDecimal();
        BigDecimal ta = a, tb = b, res(0);
        int len = ta.size() - tb.size() + a.DLEN + 1;
        if (len <= 0)
            return res;
        res._size = len;
        res._digits = new int[len];

        ta._size = abs(ta._size);
        tb._size = abs(tb._size);
        tb.align(ta.size() + 1);
        for (int i = len - 1; i >= 0; i --) {
            int num = 0;
            for (int j = 0; j < MAX_BITS; j ++) {
                tb.div2(); num <<= 1;
                if (ta >= tb) {
                    ta = num_sub(ta, tb);
                    num ^= 1;
                }
            }
            res._digits[i] = num;
        }
        if (a.is_nega() ^ b.is_nega())
            res.negate();
        res.normalize();
        return res;
    }

    BigDecimal integer() const {
        BigDecimal res = *this;
        for (int i = 0; i < DLEN && i < size(); i ++)
            res._digits[i] = 0;
        res.normalize();
        return res;
    }

    BigDecimal decimal() const {
        if (is_zero())
            return BigDecimal();
        int nlen = std::min(DLEN, size());
        BigDecimal res(0);
        res._size = nlen;
        res._digits = new int[nlen];
        memcpy(res._digits, _digits, sizeof(int) * nlen);
        if (is_nega())
            res.negate();
        return res;
    }

    friend BigDecimal abs(BigDecimal x) {
        x._size = std::abs(x._size);
        return x;
    }
};
const BigDecimal _ln2("0.693147180559945309417232121458176568075500134360255254120680");
const BigDecimal _pi("3.141592653589793238462643383279502884197169399375105820974944");
const BigDecimal _sqrt2("1.414213562373095048801688724209698078569671875376948073176680");
const BigDecimal _one("1");
const BigDecimal _pi_2 = _pi * 2;
const BigDecimal _pi_half = _pi / 2;
const BigDecimal _eps = BigDecimal::epsilon();

BigDecimal exp(BigDecimal x) {
    BigDecimal ans = 1, cnt = 1;
    for (int i = 1; ; i ++) {
        cnt = cnt * x / i;
        if (cnt.is_zero())
            break;
        ans = ans + cnt;
    }
    return ans;
}

BigDecimal sqrt(BigDecimal x) {
    BigDecimal cur = x, las = 0;
    do {
        las.swap(cur);
        cur = (las + x / las) / 2;
    } while (abs(cur - las) > _eps);
    return cur;
}

BigDecimal ln(BigDecimal x) {
    if (x < _one)
        return -ln(_one / x);
    BigDecimal coef = 0;
    int num = x.int_size();
    if (num > 1)
        x >>= num - 1, coef = _ln2 * ((long long)(num - 1) * x.MAX_BITS);
    while (x > _sqrt2)
        x.div2(), coef = coef + _ln2;
    x = (x - _one) / (x + _one);

    BigDecimal cnt = x * 2, res = cnt;
    x = x * x;
    for (int i = 3; ; i += 2) {
        cnt = cnt * x;
        BigDecimal p = cnt / i;
        if (p.is_zero())
            break;
        res = res + p;
    }
    return res + coef;
}

BigDecimal pow(BigDecimal a, BigDecimal x) {
    return exp(ln(a) * x);
}

BigDecimal sin(BigDecimal x) {
    if (x.is_nega())
        return -sin(-x);
    if (x >= _pi_2)
        x = x - (x / _pi_2).integer() * _pi_2;
    BigDecimal res = x, cur = x;
    x = x * x;
    for (long long i = 3; ; i += 2) {
        cur = cur * x / (i * (i - 1));
        if (cur.is_zero())
            break;
        cur.negate();
        res = res + cur;
    }
    return res;
}

BigDecimal cos(BigDecimal x) {
    if (x.is_nega())
        return cos(-x);
    if (x >= _pi_2)
        x = x - (x / _pi_2).integer() * _pi_2;
    BigDecimal res = 1, cur = 1;
    x = x * x;
    for (long long i = 2; ; i += 2) {
        cur = cur * x / BigDecimal(i * (i - 1));
        cur.negate();
        if (cur.is_zero())
            break;
        res = res + cur;
    }
    return res;
}

BigDecimal tan(BigDecimal x) {
    return sin(x) / cos(x);
}

BigDecimal atan(BigDecimal x) {
    static BigDecimal comp("0.1");
    if (x.is_nega())
        return - atan(-x);
    if (x > _one)
        return _pi_half - atan(_one / x);
    if (x >= comp)
        return atan((sqrt(_one + x * x) - _one) / x) * 2;
    BigDecimal cnt = x, res = x;
    x = x * x; x.negate();
    for (int i = 3; ; i += 2) {
        cnt = cnt * x;
        BigDecimal p = cnt / i;
        if (p.is_zero())
            break;
        res = res + p;
    }
    return res;
}

BigDecimal asin(BigDecimal x) {
    return atan(x / sqrt(_one - x * x));
}

BigDecimal acos(BigDecimal x) {
    return _pi_half - asin(x);
}

BigDecimal atan2(BigDecimal y, BigDecimal x) {
    if (x.is_posi())
        return atan(y / x);
    if (x.is_zero()) {
        if (y.is_nega())
            return - _pi_half;
        return _pi_half;
    }
    if (y.is_nega())
        return atan(y / x) - _pi;
    return atan(y / x) + _pi;
}
}

using namespace big;
void read(BigDecimal &bd) {
    static char ch[110];
    scanf("%s", ch);
    bd = ch;
}

BigDecimal a, b;
char op[10];

int main() {
    while (scanf("%s", op) != EOF) {
        if (strcmp(op, "add") == 0) {
            read(a); read(b); a = a + b;
        }
        else if (strcmp(op, "sub") == 0) {
            read(a); read(b); a = a - b;
        }
        else if (strcmp(op, "mul") == 0) {
            read(a); read(b); a = a * b;
        }
        else if (strcmp(op, "div") == 0) {
            read(a); read(b); a = a / b;
        }
        else if (strcmp(op, "pow") == 0) {
            read(a); read(b); a = pow(a, b);
        }
        else if (strcmp(op, "atan2") == 0) {
            read(a); read(b); a = atan2(a, b);
        }
        else if (strcmp(op, "exp") == 0) {
            read(a); a = exp(a);
        }
        else if (strcmp(op, "ln") == 0) {
            read(a); a = ln(a);
        }
        else if (strcmp(op, "sqrt") == 0) {
            read(a); a = sqrt(a);
        }
        else if (strcmp(op, "sin") == 0) {
            read(a); a = sin(a);
        }
        else if (strcmp(op, "cos") == 0) {
            read(a); a = cos(a);
        }
        else if (strcmp(op, "tan") == 0) {
            read(a); a = tan(a);
        }
        else if (strcmp(op, "asin") == 0) {
            read(a); a = asin(a);
        }
        else if (strcmp(op, "acos") == 0) {
            read(a); a = acos(a);
        }
        else if (strcmp(op, "atan") == 0) {
            read(a); a = atan(a);
        }
        else {
            break;
        }
        int prec; scanf("%d", &prec);
        a.print(prec); puts("");
    }
    return 0;
}