microPython的源码解析之 mpz.c

本文深入解析MicroPython的mpz.c源码,介绍其在微控制器环境中实现的任意精度整数运算,包括初始化、比较、加减乘除、位运算等功能。通过理解这一核心组件,有助于掌握Python解析器的实现逻辑。
摘要由CSDN通过智能技术生成

MicroPython 是一种适用于微控制器和其他受限环境的 Python 编程语言的实现。它旨在提供与 Python 3 语言的紧密兼容,同时考虑到内存和计算资源的限制。MicroPython 库是这门语言的核心组成部分,提供了一系列的模块和函数,使得开发者能够在硬件上执行各种任务。
下面将通过系列文章,逐一解读microPython,以便让读者了解掌握microPython的整个核心逻辑.,以便读者可以透过这个Python的最小内核,掌握Python解析器的核心实现逻辑,学习世界上最优秀的源码设计之一.

microPython Python最小内核源码解析

以上代码实现了多种数学运算,包括位运算、乘法、除法、取模、幂运算等,以及将大整数转换为字节序列和字符串的功能。

#include <string.h>
#include <assert.h>

// 引入大整数库的头文件
#include "py/mpz.h"

#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ

// 定义数字的大小和掩码等常量
#define DIG_SIZE (MPZ_DIG_SIZE) // 每个数字的大小
#define DIG_MASK ((MPZ_LONG_1 << DIG_SIZE) - 1) // 单个数字的掩码
#define DIG_MSB  (MPZ_LONG_1 << (DIG_SIZE - 1)) // 最高位掩码
#define DIG_BASE (MPZ_LONG_1 << DIG_SIZE) // 进制基数

/*
mpz 是一个任意精度整数类型,具有公共 API。

mpn 函数对非负整数进行操作,这些整数由一系列广义数字表示(例如,每个数字一个字)。
您还需要单独指定数组的长度。
没有公开的 mpn API。相反,这些函数被 mpz 用来实现其功能。

整数值以小端格式存储(第一个数字在内存中最先)。

规范化的定义是什么?
*/

// 移除数字数组尾部的零
static size_t mpn_remove_trailing_zeros(mpz_dig_t *oidig, mpz_dig_t *idig) {
   
    for (--idig; idig >= oidig && *idig == 0; --idig) {
   
    }
    return idig + 1 - oidig;
}

// 比较 i 和 j
// 返回 i - j 的符号
// 假设 i 和 j 已经规范化
static int mpn_cmp(const mpz_dig_t *idig, size_t ilen, const mpz_dig_t *jdig, size_t jlen) {
   
    if (ilen < jlen) {
   
        return -1;
    }
    if (ilen > jlen) {
   
        return 1;
    }

    for (idig += ilen, jdig += ilen; ilen > 0; --ilen) {
   
        mpz_dbl_dig_signed_t cmp = (mpz_dbl_dig_t)*(--idig) - (mpz_dbl_dig_t)*(--jdig);
        if (cmp < 0) {
   
            return -1;
        }
        if (cmp > 0) {
   
            return 1;
        }
    }

    return 0;
}

// 计算 i = j << n
// 返回 i 中的数字个数
// 假设 i 的内存足够;假设 j 已经规范化;假设 n > 0
// 可以有 i 和 j 指向相同的内存
static size_t mpn_shl(mpz_dig_t *idig, mpz_dig_t *jdig, size_t jlen, mp_uint_t n) {
   
    // 计算 n 需要移动的位数和部分位数
    mp_uint_t n_whole = (n + DIG_SIZE - 1) / DIG_SIZE;
    mp_uint_t n_part = n % DIG_SIZE;
    if (n_part == 0) {
   
        n_part = DIG_SIZE;
    }

    // 从高位开始移动数字
    idig += jlen + n_whole - 1;
    jdig += jlen - 1;

    // 进行位移
    mpz_dbl_dig_t d = 0;
    for (size_t i = jlen; i > 0; i--, idig--, jdig--) {
   
        d |= *jdig;
        *idig = (d >> (DIG_SIZE - n_part)) & DIG_MASK;
        d <<= DIG_SIZE;
    }

    // 存储剩余的位
    *idig = (d >> (DIG_SIZE - n_part)) & DIG_MASK;
    idig -= n_whole - 1;
    memset(idig, 0, (n_whole - 1) * sizeof(mpz_dig_t));

    // 计算结果的长度
    jlen += n_whole;
    while (jlen != 0 && idig[jlen - 1] == 0) {
   
        jlen--;
    }

    // 返回结果的长度
    return jlen;
}

// 计算 i = j >> n
// 返回 i 中的数字个数
// 假设 i 的内存足够;假设 j 已经规范化;假设 n > 0
// 可以有 i 和 j 指向相同的内存
static size_t mpn_shr(mpz_dig_t *idig, mpz_dig_t *jdig, size_t jlen, mp_uint_t n) {
   
    mp_uint_t n_whole = n / DIG_SIZE;
    mp_uint_t n_part = n % DIG_SIZE;

    if (n_whole >= jlen) {
   
        return 0;
    }

    jdig += n_whole;
    jlen -= n_whole;

    for (size_t i = jlen; i > 0; i--, idig++, jdig++) {
   
        mpz_dbl_dig_t d = *jdig;
        if (i > 1) {
   
            d |= (mpz_dbl_dig_t)jdig[1] << DIG_SIZE;
        }
        d >>= n_part;
        *idig = d & DIG_MASK;
    }

    if (idig[-1] == 0) {
   
        jlen--;
    }

    return jlen;
}

// 计算 i = j + k
// 返回 i 中的数字个数
// 假设 i 的内存足够;假设 j 和 k 已经规范化;假设 jlen >= klen
// 可以有 i, j, k 指向相同的内存
static size_t mpn_add(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen) {
   
    mpz_dig_t *oidig = idig;
    mpz_dbl_dig_t carry = 0;

    jlen -= klen;

    for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
   
        carry += (mpz_dbl_dig_t)*jdig + (mpz_dbl_dig_t)*kdig;
        *idig = carry & DIG_MASK;
        carry >>= DIG_SIZE;
    }

    for (; jlen > 0; --jlen, ++idig, ++jdig) {
   
        carry += *jdig;
        *idig = carry & DIG_MASK;
        carry >>= DIG_SIZE;
    }

    if (carry != 0) {
   
        *idig++ = carry;
    }

    return idig - oidig;
}

// 计算 i = j - k
// 返回 i 中的数字个数
// 假设 i 的内存足够;假设 j 和 k 已经规范化;假设 j >= k
// 可以有 i, j, k 指向相同的内存
static size_t mpn_sub(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen) {
   
    mpz_dig_t *oidig = idig;
    mpz_dbl_dig_signed_t borrow = 0;

    jlen -= klen;

    for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
   
        borrow += (mpz_dbl_dig_t)*jdig - (mpz_dbl_dig_t)*kdig;
        *idig = borrow & DIG_MASK;
        borrow >>= DIG_SIZE;
    }

    for (; jlen > 0; --jlen, ++idig, ++jdig) {
   
        borrow += *jdig;
        *idig = borrow & DIG_MASK;
        borrow >>= DIG_SIZE;
    }

    return mpn_remove_trailing_zeros(oidig, idig);
}

// 以下代码块在 MICROPY_OPT_MPZ_BITWISE 被定义时才会编译
#if MICROPY_OPT_MPZ_BITWISE

// 计算 i = j & k
// 返回 i 中的数字个数
// 假设 i 的内存足够;假设 j 和 k 已经规范化;假设 jlen >= klen (jlen 参数实际上不需要)
// 可以有 i, j, k 指向相同的内存
static size_t mpn_and(mpz_dig_t *idig, const mpz_dig_t *jdig, const mpz_dig_t *kdig, size_t klen) {
   
    mpz_dig_t *oidig = idig;

    for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
   
        *idig = *jdig & *kdig;
    }

    return mpn_remove_trailing_zeros(oidig, idig);
}

#endif

// 计算 i = -((-j) & (-k)) = ~((~j + 1) & (~k + 1)) + 1
// i = (j & (-k)) = (j & (~k + 1)) =  (j & (~k + 1))
// i = (-j) & k) = ((~j + 1) & k) =  ((~j + 1)&   k)
// 计算通用形式:
// i = (im ^ (((j ^ jm) + jc) & ((k ^ km) + kc))) + ic  where Xm = Xc == 0 ? 0 : DIG_MASK
// 返回 i 中的数字个数
// 假设 i 的内存足够;假设 j 和 k 已经规范化;假设 jlen >= klen
// 可以有 i, j, k 指向相同的内存
static size_t mpn_and_neg(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen,
    mpz_dbl_dig_t carryi, mpz_dbl_dig_t carryj, mpz_dbl_dig_t carryk) {
   
    mpz_dig_t *oidig = idig;
    mpz_dig_t imask = (0 == carryi) ? 0 : DIG_MASK;
    mpz_dig_t jmask = (0 == carryj) ? 0 : DIG_MASK;
    mpz_dig_t kmask = (0 == carryk) ? 0 : DIG_MASK;

    for (; jlen > 0; ++idig, ++jdig) {
   
        carryj += *jdig ^ jmask;
        carryk += (--klen <= --jlen) ? (*kdig++ ^ kmask) : kmask;
        carryi += ((carryj & carryk) ^ imask) & DIG_MASK;
        *idig = carryi & DIG_MASK;
        carryk >>= DIG_SIZE;
        carryj >>= DIG_SIZE;
        carryi >>= DIG_SIZE;
    }

    if (0 != carryi) {
   
        *idig++ = carryi;
    }

    return mpn_remove_trailing_zeros(oidig, idig);
}

#if MICROPY_OPT_MPZ_BITWISE

// 计算 i = -((-j) | (-k)) = ~((~j + 1) | (~k + 1)) + 1
// i = -(j | (-k)) = -(j | (~k + 1)) = ~(j | (~k + 1)) + 1
// i = -((-j) | k) = -((~j + 1) | k) = ~((~j + 1) | k) + 1
// 计算通用形式:
// i = ~(((j ^ jm) + jc) | ((k ^ km) + kc)) + 1  where Xm = Xc == 0 ? 0 : DIG_MASK
// 返回 i 中的数字个数
// 假设 i 的内存足够;假设 j 和 k 已经规范化;假设 jlen >= klen
// 可以有 i, j, k 指向相同的内存

static size_t mpn_or_neg(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen,
    mpz_dbl_dig_t carryj, mpz_dbl_dig_t carryk) {
   
    mpz_dig_t *oidig = idig;
    mpz_dbl_dig_t carryi = 1;
    mpz_dig_t jmask = (0 == carryj) ? 0 : DIG_MASK;
    mpz_dig_t kmask = (0 == carryk) ? 0 : DIG_MASK;

    for (; jlen > 0; ++idig, ++jdig) {
   
        carryj += *jdig ^ jmask;
        carryk += (--klen <= --jlen) ? (*kdig++ ^ kmask) : kmask;
        carryi += ((carryj | carryk) ^ DIG_MASK) & DIG_MASK;
        *idig = carryi & DIG_MASK;
        carryk >>= DIG_SIZE;
        carryj >>= DIG_SIZE;
        carryi >>= DIG_SIZE;
    }

    // 至少 j 或 k 之一必须是非零的,所以上面的循环至少运行一次。
    // 如果 carryi 在这里不为零,它必须在每次循环迭代结束时都为零。
    // 因此,carryi 必须每次都溢出,即 carryi += 0xff..ff。
    // 所以 carryj|carryk 必须在每次迭代中在 DIG_MASK 位上为零。
    // 但考虑到 j,k 所有可能的符号情况,这是不可能的。
    assert(carryi == 0);

    return mpn_remove_trailing_zeros(oidig, idig);
}

#else

// 计算 i = -((-j) | (-k)) = ~((~j + 1) | (~k + 1)) + 1
// i = -(j | (-k)) = -(j | (~k + 1)) = ~(j | (~k + 1)) + 1
// i = -((-j) | k) = -((~j + 1) | k) = ~((~j + 1) | k) + 1
// 计算通用形式:
// i = ~(((j ^ jm) + jc) | ((k ^ km) + kc)) + 1  where Xm = Xc == 0 ? 0 : DIG_MASK
// 返回 i 中的数字个数
// 假设 i 的内存足够;假设 j 和 k 已经规范化;假设 jlen >= klen
// 可以有 i, j, k 指向相同的内存

static size_t mpn_or_neg(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen,
    mpz_dbl_dig_t carryi, mpz_dbl_dig_t carryj, mpz_dbl_dig_t carryk) {
   
    mpz_dig_t *oidig = idig;
    mpz_dig_t imask = (0 == carryi) ? 0 : DIG_MASK;
    mpz_dig_t jmask = (0 == carryj) ? 0 : DIG_MASK;
    mpz_dig_t kmask = (0 == carryk) ? 0 : DIG_MASK;

    for (; jlen > 0; ++idig, ++jdig) {
   
        carryj += *jdig ^ jmask;
        carryk += (--klen <= --jlen) ? (*kdig++ ^ kmask) : kmask;
        carryi += ((carryj | carryk) ^ imask) & DIG_MASK;
        *idig = carryi & DIG_MASK;
        carryk >>= DIG_SIZE;
        carryj >>= DIG_SIZE;
        carryi >>= DIG_SIZE;
    }

    // 参见上面 mpn_or_neg 的注释,为什么 carryi 必须为零。
    assert(carryi == 0);

    return mpn_remove_trailing_zeros(oidig, idig);
}

#endif

#if MICROPY_OPT_MPZ_BITWISE

// 计算 i = j ^ k
// 返回 i 中的数字个数
// 假设 i 的内存足够;假设 j 和 k 已经规范化;假设 jlen >= klen
// 可以有 i, j, k 指向相同的内存
static size_t mpn_xor(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen) {
   
    mpz_dig_t *oidig = idig;

    jlen -= klen;

    for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
   
        *idig = *jdig ^ *kdig;
    }

    for (; jlen > 0; --jlen, ++idig, ++jdig) {
   
        *idig = *jdig;
    }

    return mpn_remove_trailing_zeros(oidig, idig);
}

#endif

// 计算 i = (-j) ^ (-k) = ~(j - 1) ^ ~(k - 1) = (j - 1) ^ (k - 1)
// i = -(j ^ (-k)) = -(j ^ ~(k - 1)) = ~(j ^ ~(k - 1)) + 1 = (j ^ (k - 1)) + 1
// i = -((-j) ^ k) = -(~(j - 1) ^ k) = ~(~(j - 1) ^ k) + 1 = ((j - 1) ^ k) + 1
// 计算通用形式:
// i = ((j - 1 + jc) ^ (k - 1 + kc)) + ic
// 返回 i 中的数字个数
// 假设 i 的内存足够;假设 j 和 k 已经规范化;假设 jlen >= klen
// 可以有 i, j, k 指向相同的内存
static size_t mpn_xor_neg(mpz_dig_t *idig, const mpz_dig_t *jdig, size_t jlen, const mpz_dig_t *kdig, size_t klen,
    mpz_dbl_dig_t carryi, mpz_dbl_dig_t carryj, mpz_dbl_dig_t carryk){
   
    mpz_dig_t *oidig = idig;

    for (; jlen > 0; ++idig, ++jdig) {
   
        carryj += *jdig + DIG_MASK;
        carryk += (--klen <= --jlen) ? (*kdig++ + DIG_MASK) : DIG_MASK;
        carryi += (carryj ^ carryk) & DIG_MASK;
        *idig = carryi & DIG_MASK;
        carryk >>= DIG_SIZE;
        carryj >>= DIG_SIZE;
        carryi >>= DIG_SIZE;
    }

    if (0 != carryi) {
   
        *idig++ = carryi;
    }

    return
  • 20
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

openwin_top

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值