Skip to content

Instantly share code, notes, and snippets.

@liquidhelium
Created March 7, 2026 07:31
Show Gist options
  • Select an option

  • Save liquidhelium/68226a052d1a496aabb76bb720eaa0ec to your computer and use it in GitHub Desktop.

Select an option

Save liquidhelium/68226a052d1a496aabb76bb720eaa0ec to your computer and use it in GitHub Desktop.
#set page(width: auto, height: auto)
#set text(font: ("Libertinus Serif", "Noto Serif CJK SC"))
#set text(12pt)
#let binary-div(n) = {
let val = n
let rows = ()
// 循环计算直到商为 0
while val > 0 {
let remainder = calc.rem(val, 2)
// 使用 int() 确保除法结果向下取整
let next_val = int(val / 2)
// 使用 += 将这三个元素拼接到 rows 数组中
rows += (
$2$,
table.cell(
stroke: (left: 1pt, bottom: 1pt),
align: right,
[#val],
),
align(left)[#remainder],
)
val = next_val
}
// 添加最后的商 0
rows += (
"",
align(right)[0],
"",
)
block(stack(
dir: ltr,
spacing: 1em,
// 渲染表格
table(
columns: (2em, 3em, 3em),
stroke: none,
align: center + horizon,
..rows
),
))
}
// // 使用示例
// // #set page(width: auto, height: auto, margin: 1cm)
// #binary-div(3)
// $(3)_10 = (11)_2$
#let frac-to-binary-math(num, max-digits: 10) = {
let val = num
let steps = ($num$,)
let result = ""
for i in range(max-digits) {
if val == 0 { break }
let product = val * 2
let int-part = int(product)
let next-val = product - int-part
// 构造每一行的数学内容:times 2 &= 剩余小数 + & 整数位
// 这里使用 calc.round 避免浮点数精度带来的微小误差(如 0.0000000001)
steps.push(
$times 2 &= #calc.round(next-val, digits: 10) + &#int-part$,
)
result += str(int-part)
val = next-val
}
// 将所有行用换行符 \ 连接
let formula = steps.join($ \ $)
block[
#math.equation(block: true, formula)
$(#num)_(10) = (0.#result)_(2)$
]
}
// #frac-to-binary-math(0.215, max-digits: 6)
// $(3.215)_10 = (11.001101)_2$
#let bin-to-dec-math(bin-str) = {
// 处理输入,识别整数和小数部分
let parts = str(bin-str).split(".")
let int-part = parts.at(0)
let frac-part = if parts.len() > 1 { parts.at(1) } else { "" }
let terms = ()
let total-val = 0.0
// 1. 处理整数部分 (从右往左,幂次从 0 开始)
let int-len = int-part.len()
for i in range(int-len) {
let digit = int-part.at(int-len - 1 - i)
let val = int(digit)
if val != 0 {
terms.push($#digit times 2^#i$)
total-val += val * calc.pow(2, i)
} else {
terms.push($#digit times 2^#i$)
}
}
// 翻转整数项,使其符合书写习惯 (从高位到低位)
terms = terms.rev()
// 2. 处理小数部分 (从左往右,幂次从 -1 开始)
for i in range(frac-part.len()) {
let digit = frac-part.at(i)
let val = int(digit)
let power = -(i + 1)
if val != 0 {
terms.push($#digit times 2^#power$)
total-val += val * calc.pow(2, power)
} else {
terms.push($#digit times 2^#power$)
}
}
// 将所有项用 + 连接
let expansion = terms.join($ + $)
block[
$
(#bin-str)_2 & = #expansion \
& = #total-val
$
$ (#bin-str)_2 = (#total-val)_(10) $
]
}
// #bin-to-dec-math("0.10011101")
#let dec-to-hex-int(n) = {
let val = n
let rows = ()
let hex-digits = "0123456789ABCDEF"
let result = ""
while val > 0 {
let rem = calc.rem(val, 16)
let hex-char = hex-digits.at(rem)
let next_val = int(val / 16)
rows += (
$16$,
table.cell(
stroke: (left: 1pt, bottom: 1pt),
align: right,
[#val],
),
// 如果余数 >= 10,同时显示数字和字母
align(left)[$dots.c$ #rem #if rem >= 10 [(#hex-char)]],
)
result = hex-char + result
val = next_val
}
rows += ("", align(right)[0], "")
block(stack(
spacing: 2em,
table(columns: (2em, 4em, 5em), stroke: none, align: center + horizon, ..rows),
[
$ (#n)_(10) = (#result)_(16) $
],
))
}
#let hex-to-dec-math(hex-str) = {
let hex-str = str(hex-str)
let hex-map = (
"0": 0,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"9": 9,
"A": 10,
"B": 11,
"C": 12,
"D": 13,
"E": 14,
"F": 15,
)
// 处理输入,识别整数和小数部分
let parts = hex-str.split(".")
let int-part = parts.at(0)
let frac-part = if parts.len() > 1 { parts.at(1) } else { "" }
let terms = ()
let total-val = 0.0
// 1. 处理整数部分 (从高位到低位)
let int-len = int-part.len()
for i in range(int-len) {
let char = int-part.at(i)
let val = hex-map.at(char)
let power = int-len - 1 - i
// 构造数学项,如果原字符是 A-F,在展开中显示其数值
let display-val = if "ABCDEF".contains(char) {
$#val (#char)$
} else {
$#val$
}
terms.push($#display-val times 16^#power$)
total-val += val * calc.pow(16, power)
}
// 2. 处理小数部分 (从左往右,幂次从 -1 开始)
for i in range(frac-part.len()) {
let char = frac-part.at(i)
let val = hex-map.at(char)
let power = -(i + 1)
let display-val = if "ABCDEF".contains(char) {
$#val (#char)$
} else {
$#val$
}
terms.push($#display-val times 16^#power$)
total-val += val * calc.pow(16, power)
}
let expansion = terms.join($ + $)
block[
$
(#hex-str)_(16) & = #expansion \
& = #total-val
$
$ (#hex-str)_(16) = (#total-val)_(10) $
]
}
#let bin-to-oct-math(bin-str) = {
let s = str(bin-str)
// 1. 自动补齐前导 0,使其长度为 3 的倍数
let pad-len = calc.rem(3 - calc.rem(s.len(), 3), 3)
let padded-s = "0" * pad-len + s
let groups = ()
let oct-digits = ()
// 2. 按照 3 位一组进行切分和计算
for i in range(0, padded-s.len(), step: 3) {
let group = padded-s.slice(i, i + 3)
let b2 = int(group.at(0)) * 4
let b1 = int(group.at(1)) * 2
let b0 = int(group.at(2)) * 1
let oct-val = b2 + b1 + b0
groups.push(group)
oct-digits.push(str(oct-val))
}
let result = oct-digits.join("")
block[
// 第一行:展示分组
$
underbrace(#groups.at(0), #oct-digits.at(0))
#for i in range(1, groups.len()) { [ $#underbrace(groups.at(i), oct-digits.at(i))$ ] }
$
// 第二行:最终结论
$ (#bin-str)_2 = (#result)_8 $
]
}
// 二进制转八进制 (三位一组)
// #bin-to-oct-math("1101")
// #bin-to-oct-math("1000110110101")
#let hex-to-oct-math(hex-str) = {
let hex-str = str(hex-str)
let hex-map = (
"0": "0000",
"1": "0001",
"2": "0010",
"3": "0011",
"4": "0100",
"5": "0101",
"6": "0110",
"7": "0111",
"8": "1000",
"9": "1001",
"A": "1010",
"B": "1011",
"C": "1100",
"D": "1101",
"E": "1110",
"F": "1111",
)
// 1. 十六进制转二进制 (4-bit chunks)
let bin-full = ""
let hex-steps = ()
for char in hex-str {
let b = hex-map.at(char)
bin-full += b
hex-steps.push($underbrace(#b, #char)$)
}
// 2. 二进制转八进制 (3-bit chunks)
// 去掉前导 0 以便重新分组
let bin-clean = bin-full.replace(regex("^0+"), "")
if bin-clean == "" { bin-clean = "0" }
// 补足 3 的倍数
let pad-len = calc.rem(3 - calc.rem(bin-clean.len(), 3), 3)
let padded-bin = "0" * pad-len + bin-clean
let oct-steps = ()
let oct-digits = ""
for i in range(0, padded-bin.len(), step: 3) {
let group = padded-bin.slice(i, i + 3)
let val = int(group.at(0)) * 4 + int(group.at(1)) * 2 + int(group.at(2)) * 1
oct-steps.push($underbrace(group, str(val))$)
oct-digits += str(val)
}
block[
// 第一步:Hex -> Bin
$ (#hex-str)_(16) = #hex-steps.join([ ]) $
#v(1em)
// 第二步:Bin -> Oct
$ #oct-steps.join([ ]) = (#oct-digits)_8 $
#v(1em)
// 结论
$ (#hex-str)_(16) = (#oct-digits)_8 $
]
}
#let dec-to-18-int(n) = {
let val = n
let base = 18
let rows = ()
// 扩展映射表:10-17 对应 A-H
let digits-map = "0123456789ABCDEFGH"
let result = ""
// 处理输入为 0 的特殊情况
if val == 0 {
result = "0"
}
while val > 0 {
let rem = calc.rem(val, base)
let char = digits-map.at(rem)
let next_val = int(val / base)
rows += (
[#base],
table.cell(
stroke: (left: 1pt, bottom: 1pt),
align: right,
[#val],
),
// 标注余数,如果超过 10 则显示字母对应关系
align(left)[$dots.c$ #rem #if rem >= 10 [(#char)]],
)
result = char + result
val = next_val
}
// 补上最后的商 0
rows += ("", align(right)[0], "")
block(stack(
spacing: 2em,
table(
columns: (2.5em, 5em, 5em),
stroke: none,
align: center + horizon,
..rows
),
[
// 从下往上读 $arrow.t$ \
$ (#n)_(10) = (#result)_(18) $
],
))
}
#let base13-to-dec(hex-str) = {
let s = str(hex-str)
// 自定义映射表:X=10, Y=11, Z=12
let map = (
"0": 0,
"1": 1,
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"9": 9,
"X": 10,
"Y": 11,
"Z": 12,
)
let terms = ()
let total = 0
let len = s.len()
for i in range(len) {
let char = s.at(i)
let val = map.at(char)
let power = len - 1 - i
// 构造展示项:例如 10(X) * 13^1
let display = if "XYZ".contains(char) { $#val (#char)$ } else { [#val] }
terms.push($#display times 13^#power$)
total += val * calc.pow(13, power)
}
$
(#hex-str)_(13) & = #terms.join($ + $) \
& = #total
$
}
#let complement-steps(num, bits: 16) = {
// 辅助函数:将整数转为固定位数的二进制字符串
let to-bin-str(n, width) = {
let s = str(calc.abs(n), base: 2)
let padded = "0" * (width - s.len()) + s
// 每 4 位加一个空格
let formatted = ""
for i in range(width) {
formatted += padded.at(i)
if calc.rem(i + 1, 4) == 0 and i + 1 != width { formatted += " " }
}
return formatted
}
// 1. 数值列
let col-val = [#num]
// 2. 绝对值的二进制表示列
let abs-bin = str(calc.abs(num), base: 2)
let col-abs = [#abs-bin(绝对值)]
// 3. 原码列
let raw-bin = to-bin-str(num, bits)
let col-raw = [#raw-bin]
// 4. 补码计算过程列
let col-comp = if num >= 0 {
raw-bin
} else {
// 负数计算逻辑
let abs-val = calc.abs(num)
// 反码:原码每一位取反(针对原码字符串,保持空格)
let inverse-str = ""
for char in raw-bin {
if char == "0" { inverse-str += "1" } else if char == "1" { inverse-str += "0" } else { inverse-str += char }
}
// 补码:计算结果
let comp-val = calc.pow(2, bits) - abs-val
let comp-str = to-bin-str(comp-val, bits)
// 模拟图片中的加 1 竖式
align(right)[
#inverse-str \
$+$ #h(1fr) $1$ \
#line(length: 100%, stroke: 0.5pt)
#text(red)[#comp-str]
]
}
// 返回表格行数据
return (col-val, col-abs, col-raw, col-comp)
}
// 主绘图表格函数
#let complement-table(numbers, bits: 16) = {
table(
columns: (auto, auto, auto, auto),
inset: 10pt,
align: horizon + center,
stroke: (x, y) => if y == 0 { (bottom: 1pt + black) } else { (bottom: 0.5pt + black) },
// 表头
[*数值*], [*绝对值的二进制表示*], [*原码*], [*补码*],
// 填充数据行
..numbers.map(n => complement-steps(n, bits: bits)).flatten(),
)
}
#let bin-complement-to-dec(bin-strs, bits: 16) = {
// 1. 辅助函数:手动实现二进制字符串转十进制
let bin-to-int(s) = {
let val = 0
let clean = s.replace(" ", "")
let char-list = clean.clusters()
let len = char-list.len()
for i in range(len) {
if char-list.at(i) == "1" {
val += calc.pow(2, len - 1 - i)
}
}
return val
}
// 2. 辅助函数:格式化显示(每 4 位加空格)
let format-bin(s) = {
let clean = s.replace(" ", "").replace("-", "")
let res = ""
let chars = clean.clusters()
for i in range(chars.len()) {
res += chars.at(i)
if calc.rem(i + 1, 4) == 0 and i + 1 != chars.len() { res += " " }
}
res
}
// 3. 辅助函数:手动补齐长度 (替代不规范的 pad)
let pad-left(s, target-len) = {
let current-len = s.len()
if current-len < target-len {
return "0" * (target-len - current-len) + s
}
return s
}
let rows = ()
for b-str in bin-strs {
let clean-b = b-str.replace(" ", "").replace("-", "")
// 补码最高位为 1 则为负数 (假设长度对齐)
let is-negative = clean-b.clusters().at(0) == "1"
// 1. 补码列
let col-comp = format-bin(clean-b)
// 2. 转换步骤列
let col-steps = if not is-negative {
[正数:直接转换]
} else {
let val = bin-to-int(clean-b)
// 模拟 -1 操作
let minus-one-val = val - 1
let minus-one-str = pad-left(str(minus-one-val, base: 2), bits)
// 模拟 取反 操作
let inverse-str = ""
for char in minus-one-str.clusters() {
if char == "0" { inverse-str += "1" } else if char == "1" { inverse-str += "0" }
}
align(right)[
#format-bin(clean-b) (补码) \
$-$ #h(1fr) $1$ \
#line(length: 100%, stroke: 0.5pt)
#format-bin(minus-one-str) (反码) \
#v(0.5em)
取反后:#format-bin(inverse-str) (原码)
]
}
// 3. 绝对值与最终数值
let total-val = bin-to-int(clean-b)
let final-abs = 0
let final-dec = 0
if is-negative {
final-abs = calc.pow(2, bits) - total-val
final-dec = -final-abs
} else {
final-abs = total-val
final-dec = total-val
}
rows += (col-comp, col-steps, [#final-abs], [*#final-dec*])
}
table(
columns: (auto, auto, auto, auto),
inset: 10pt,
align: horizon + center,
stroke: (x, y) => if y == 0 { (bottom: 1pt + rgb("#e4007f")) } else { (bottom: 0.5pt + gray) },
// 修正了 bits-bit 的变量名错误
[*补码 (#bits bit)*], [*转换步骤 (-1, 取反)*], [*绝对值*], [*十进制*],
..rows,
)
}
#bin-complement-to-dec(("1101 1011",), bits: 8)
#complement-table((-37,), bits: 8)
== 13 \~ 10
#base13-to-dec("X1Y2Z3")
== 16 \~ 8
#hex-to-oct-math("BC4A8F")
== 16 \~ 10
#dec-to-hex-int(2912)
== 10 \~ 16
#dec-to-hex-int(1145)
== 2 \~ 10
#bin-to-dec-math("101.101")
== 10 \~ 2
#binary-div(10)
== 10 \~ 18
#dec-to-18-int(1145)
== 10(小数)
#frac-to-binary-math(0.625)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment