【C】原码和补码以及环形坐标取模算法

一切数字在计算机底层都是以补码的形式存储和计算,补码天然是无符号的。但为了人们方便数学计算,利用计算机数据溢出绕尾的特性,提出了有符号数,也就是原码。补码层面的计算也是符合数学逻辑,比如unsigned int 和 int计算,都先转成unsigned int计算,结果是unsigned int类型,和你都当成有符号类型去计算,结果是一样的。属于是数学统一下的一体两面。

以有符号单字节为例,无符号原码补码相同:

原码 5(0000 0101)的补码就是 5(0000 0101)

原码-5(1000 0101)的补码就是251(1111 1011)

正数的补码和原码相同

负数符号位不变,其余取反加1,就是补码。补码符号位不变,其余取反加1又变成原码。

负数的补码也可以通过"0加"获得,0等于256,0+(-5) = 256 - 5 = 251,模型如下图:

在C语言的公式计算中,可以先不考虑有符号无符号,单纯以数学逻辑去计算,最后看结果,中间只要注意算出的数会不会因为整型提升丢失符号位即可。https://kdocs.cn/l/cgXXFjtyTtfa

补充:

0-(-5) = 256 - 251 = 5,可以通过"0减"的方式算一个负数的绝对值。

模运算补充:

数学语境中的除运算(/)和模运算(%)是除法带余数和取整,规则上和Python中的运算是一样的,但是Python有(//)用于取整除。C语言中只有除运算(/)和模运算(%)是取整除和求余数。

被除数 = 商 × 除数 + 余数(记为:a = q × b + r),a / b = q 余 r。

商的符号由a和b决定,同号为正,异号为负。

Python算法上余数符号和除数(M)一致。C语言算法上余数符号和被除数(N)一致。

两者运算规则上的核心差异:python的商向下取整,C语言的商向0取整。

例子:

Python:-10 / 3 = -4 余 2,-10 = 3 * -4 + 2,因为-10 / 3等于-3.几,向下取整到-4。

C:-10 / 3 = -3 余 -1,-10 = 3 * -3 + -1,商向0取整到-3。

Python:10 / -3 = -4 余 -2,10 = -3 * -4 + -2

C:10 / -3 = -3 余 1,10 = -3 * -3 + 1

Python:-10 / -3 = 3 余 -1,-10 = -3 * 3 + -1,因为-10 / -3等于3.几,向下取整到3。

C:-10 / -3 = 3 余 -1,-10 = -3 * 3 + -1,因为-10 / -3等于3.几,向0取整到3。

Python:10 / 3 = 3 余 1,10 = 3 * 3 + 1

C:10 / 3 = 3 余 1,10 = 3 * 3 + 1

Python:-3 / 1000 = -1 余 997,-3 = 1000 * -1 + 997,因为-3 / 1000等于-0.几,向下取整到-1。

C:-3 / 1000 = 0 余 -3,-3 = 1000 * 0 + -3,商向0取整到0。

Python中商为正数时和C语言中是一样的,但是商小于等于0就不一样,故在C语言中当M是正值,N是负值的时候一定要用公式(N % M + M) % M,此时的结果和Python是一致的。模运算的本质就是把N拉到[-M+1, M-1]的范围内。且余数符号和除数一致,当M为正数,则余数范围[0, M-1]。

补充:

数据在[0, M-1]的范围变化,总共有M个数据。从S点写LEN个数据到E点,E点是干净未处理的。

  • 有两个点S和E,从S加LEN到E,计算E,本质算法是E = (S + LEN) >= M ? ((S + LEN) - M) : (S + LEN);

对应取模算法:E = (S + LEN) % M,(S + LEN)是正值,直接%就行。

  • 有两个点S和E,从S减LEN到E,计算E,本质算法是E = (S - LEN) < 0 ? ((S - LEN) + M) : (S - LEN);

对应取模算法:E = (S - LEN + M) % M,(S - LEN + M)是正值,直接%就行。

  • 有两个点S和E,从S加LEN到E,计算LEN,本质算法是LEN = (E - S) >= 0 ? (E - S) : ((E - S) + M);

对应取模算法:LEN = ((E - S) % M + M) % M,(E - S)可能是负值,直接%的商也是负值,所以套用公式。

上述是数组类,LEN = 3是包含索引0在内。

如果是定时器节拍类,举例:实时时间R,之前时间C,超时时间D。LEN = 3是包含索引3在内,如下图。但两者本质上是一样的,只不过物理内容所在一个偏左一个偏右。

补充:上述"数据在[0, M-1]"的例子中,M是正数,"S - LEN"或"E - S"的绝对值不大于M:

  • "E = (S + LEN) % M"简化成"E = (S + LEN) % M";
  • "E = (S - LEN + M) % M"简化成"E = (S - LEN + M) % M",算"上N条"用法;
  • "LEN = ((E - S) % M + M) % M"简化成"LEN = (E - S + M) % M";
  • https://www.kdocs.cn/l/cp1DT0sq6ltW,见20240709,如果M是特殊的2的幂次数(整型变量的最大值+1),计算时则无需求余数,变量具有1个天然的模或者回绕点。
相关推荐
matlab_xiaowang4 分钟前
Redux 入门:JavaScript 可预测状态管理库
开发语言·javascript·其他·ecmascript
Merlos_wind12 分钟前
HashMap详解
算法·哈希算法·散列表
虹科网络安全16 分钟前
艾体宝干货|数据复制详解:类型、原理与适用场景
java·开发语言·数据库
axng pmje41 分钟前
Java语法进阶
java·开发语言·jvm
汉克老师44 分钟前
GESP2025年3月认证C++五级( 第三部分编程题(1、平均分配))
c++·算法·贪心算法·排序·gesp5级·gesp五级
老前端的功夫1 小时前
【Java从入门到入土】28:Stream API:告别for循环的新时代
java·开发语言·python
qq_435287921 小时前
第9章 夸父逐日与后羿射日:死循环与进程终止?十个太阳同时值班的并行冲突
java·开发语言·git·死循环·进程终止·并行冲突·夸父逐日
止语Lab1 小时前
从手动到框架:Go DI 演进的三个拐点
开发语言·后端·golang
yaoxin5211231 小时前
397. Java 文件操作基础 - 创建常规文件与临时文件
java·开发语言·python
小短腿的代码世界1 小时前
Qt日志系统深度解析:从qDebug到企业级日志框架
开发语言·qt