【学Rust写CAD】31 muldiv255函数(muldiv255.rs)

源码

rust 复制代码
// Calculates floor(a*b/255 + 0.5)
#[inline]
pub fn muldiv255(a: u32, b: u32) -> u32 {
    // The deriviation for this formula can be
    // found in "Three Wrongs Make a Right" by Jim Blinn.
    let tmp = a * b + 128;
    (tmp + (tmp >> 8)) >> 8
}

代码分析

这个 muldiv255 函数是一个高效的实现,用于计算 floor(a * b / 255 + 0.5),也就是将两个无符号整数 a 和 b 相乘,然后除以 255,并进行四舍五入。这种计算在图像处理中很常见,比如在 alpha 混合(alpha blending)或颜色空间转换时。

步骤分解:
  1. a * b + 128:
  • 首先计算 a * b,然后加上 128。这个 128 的作用是为了后续的四舍五入(因为 255 / 2 = 127.5,所以加 128 可以确保正确的舍入)。
  1. tmp + (tmp >> 8):
  • tmp >> 8 相当于 tmp / 256(右移 8 位相当于除以 2^8)。

  • 然后 tmp + (tmp >> 8) 相当于 tmp + tmp / 256。

  1. 8:

  • 最后右移 8 位,相当于除以 256。

为什么这个公式有效?

这个方法的数学原理来自 Jim Blinn 的文章 "Three Wrongs Make a Right",它利用了近似计算来避免昂贵的除法运算(因为整数除法在 CPU 上通常比乘法和移位慢)。

  • 原本的计算是 (a * b + 127) / 255(四舍五入)。

  • 但 (a * b + 128) * 257 / 65536(即 (tmp + (tmp >> 8)) >> 8)是一个近似计算,可以高效地用移位和加法实现。

数学推导:

  1. 我们希望计算 (a * b + 127) / 255(四舍五入)。

  2. 可以近似为 (a * b + 128) * (256 + 1) / 65536(因为 255 ≈ 65536 / 257)。

展开后:

复制代码
(a * b + 128) * (256 + 1) / 65536
= (256*(a*b + 128) + (a*b + 128)) / 65536
= ( (a*b + 128) << 8 + (a*b + 128) ) >> 16
= (tmp << 8 + tmp) >> 16
= (tmp + (tmp >> 8)) >> 8

最终得到 (tmp + (tmp >> 8)) >> 8。

示例

假设 a = 100, b = 200:

  1. tmp = 100 * 200 + 128 = 20000 + 128 = 20128

  2. tmp >> 8 = 20128 / 256 = 78

  3. tmp + (tmp >> 8) = 20128 + 78 = 20206

20206 >> 8 = 20206 / 256 = 78(实际 100 * 200 / 255 ≈ 78.431,四舍五入得 78)

为什么不用 (a * b + 127) / 255?

虽然 (a * b + 127) / 255 也能得到正确结果,但:

  • 除法 (/ 255) 在 CPU 上通常比移位和加法慢。

  • 这个优化方法避免了除法,改用移位和加法,性能更高。

适用场景

  • 图像处理(如 alpha 混合、颜色计算)。

  • 需要高效计算 (a * b) / 255 并四舍五入的情况。

总结

这个函数巧妙地利用移位和加法来近似计算 (a * b) / 255 并四舍五入,避免了昂贵的除法运算,是一种高效的位操作优化方法。

相关推荐
刘布斯yy6 小时前
新写了个直播录制工具,可录制抖音快手斗鱼直播
rust·音视频·直播录制
恋喵大鲤鱼9 小时前
Rust 中的字符串 slice 是什么?
rust
迷渡1 天前
用 Rust 重写的 Bun 有 13365 个 unsafe!
开发语言·后端·rust
咸甜适中1 天前
rust语言学习笔记Trait(九)PartialEq、 Eq(相等比较)
笔记·学习·rust
恋喵大鲤鱼1 天前
Rust 中 package crate 和 module 的关系
rust
Rust研习社1 天前
Rust 官方拟定 LLM 政策,防止 LLM 污染开源社区?
开发语言·后端·ai·rust·开源
techdashen1 天前
Async Rust 近况补课:从 `async-trait` 到原生 async trait
网络·算法·rust
techdashen1 天前
在 Async Rust 中实现请求合并(Request Coalescing)
开发语言·后端·rust
咸甜适中2 天前
rust语言学习笔记Trait(八)Iterator(迭代器)
笔记·学习·rust