c语⾔mod函数例⼦,c语⾔快速学习(⽰例代码)
原码, 反码, 补码的基础概念和计算⽅法.
在探求为何机器要使⽤补码之前, 让我们先了解原码, 反码和补码的概念.对于⼀个数, 计算机要使⽤⼀定的编码⽅式进⾏存储. 原码, 反码, 补码是机器存储⼀个具体数字的编码⽅式.
1. 原码
原码就是符号位加上真值的绝对值, 即⽤第⼀位表⽰符号, 其余位表⽰值. ⽐如如果是8位⼆进制:
[+1]原 = 0000 0001
[-1]原 = 1000 0001
第⼀位是符号位. 因为第⼀位是符号位, 所以8位⼆进制数的取值范围就是:
[1111 1111 , 0111 1111]==>[-127 , 127]
2. 反码
反码的表⽰⽅法是:
正数的反码是其本⾝
负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
3. 补码
补码的表⽰⽅法是:
正数的补码就是其本⾝
负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
三. 为何要使⽤原码, 反码和补码
计算机可以有三种编码⽅式表⽰⼀个数. 对于正数因为三种编码⽅式的结果都相同:
[+1] = [00000001]原 = [00000001]反 = [00000001]补
所以不需要过多解释. 但是对于负数:
[-1] = [10000001]原 = [11111110]反 = [11111111]补
可见原码, 反码和补码是完全不同的. 为何还会有反码和补码呢?
⾸先, 因为⼈脑可以知道第⼀位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本⽂最开头).
但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别"符号位"显然会让计算机的基础电路设计变得⼗分复杂! 于是⼈们想出了将符号位也参与运算的⽅法.
根据运算法则减去⼀个正数等于加上⼀个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法⽽没有减法, 这样计算机运算的设计就更简单了.
于是⼈们开始探索 将符号位参与运算, 并且只保留加法的⽅法. ⾸先来看原码:
计算⼗进制的表达式: 1-1=0
为了解决原码做减法的问题, 出现了反码:
1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]
原 = -0
发现⽤反码计算减法, 结果的真值部分是正确的. ⽽唯⼀的问题其实就出现在"0"这个特殊的数值上. 虽然⼈们理解上+0和-0是⼀样的, 但是0带符号是没有任何意义的. ⽽且会有[0000 0000]原和[1000 0000]原两个编码表⽰0.
于是补码的出现, 解决了0的符号以及两个编码的问题:
1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
这样0⽤[0000 0000]表⽰, ⽽以前出现问题的-0则不存在了.⽽且可以⽤[1000 0000]表⽰-128:
(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补
-1-127的结果应该是-128, 在⽤补码运算的结果中, [1000 0000]补 就是-128. 但是注意因为实际上是使⽤以前的-0的补码来表⽰-128,所以-128并没有原码和反码表⽰.(对-128的补码表⽰[1000 0000]补算出来的原码是[0000 0000]原, 这是不正确的)
使⽤补码, 不仅仅修复了0的符号以及存在两个编码的问题, ⽽且还能够多表⽰⼀个最低数. 这就是为什么8位⼆进制, 使⽤原码或反码表⽰的范围为[-127, +127], ⽽使⽤补码表⽰的范围为[-128, 127].
因为机器使⽤补码, 所以对于编程中常⽤到的32位int类型, 可以表⽰范围是: [-231, 231-1] 因为第⼀位表⽰的是符号位.⽽使⽤补码表⽰时⼜可以多保存⼀个最⼩值.
四 原码, 反码, 补码 再深⼊
计算机巧妙地把符号位参与运算, 并且将减法变成了加法, 背后蕴含了怎样的数学原理呢?
将钟表想象成是⼀个1位的12进制数. 如果当前时间是6点, 我希望将时间设置成4点, 需要怎么做呢?我们可以:
1. 往回拨2个⼩时: 6 - 2 = 4
2. 往前拨10个⼩时: (6 + 10) mod 12 = 4
3. 往前拨10+12=22个⼩时: (6+22) mod 12 =4
2,3⽅法中的mod是指取模操作, 16 mod 12 =4 即⽤16除以12后的余数是4.
所以钟表往回拨(减法)的结果可以⽤往前拨(加法)替代!
现在的焦点就落在了如何⽤⼀个正数, 来替代⼀个负数. 上⾯的例⼦我们能感觉出来⼀些端倪, 发现⼀些规律. 但是数学是严谨的. 不能靠感觉.
⾸先介绍⼀个数学中相关的概念: 同余
同余的概念
两个整数a,b,若它们除以整数m所得的余数相等,则称a,b对于模m同余
记作 a ≡ b (mod m)
读作 a 与 b 关于模 m 同余。
举例说明:
4 mod 12 = 4
16 mod 12 = 4
28 mod 12 = 4
所以4, 16, 28关于模 12 同余.
c语言如何去学负数取模
正数进⾏mod运算是很简单的. 但是负数呢?
下⾯是关于mod运算的数学定义:
上⾯是截图, "取下界"符号不到如何输⼊(word中粘贴过来后乱码). 下⾯是使⽤"L"和"J"替换上图的"取下界"符号:
x mod y = x - y L x / y J
上⾯公式的意思是:
x mod y等于 x 减去 y 乘上 x与y的商的下界.
以 -3 mod 2 举例:
-3 mod 2
= -3 - 2xL -3/2 J
= -3 - 2xL-1.5J
= -3 - 2x(-2)
= -3 + 4 = 1
所以:
(-2) mod 12 = 12-2=10
(-4) mod 12 = 12-4 = 8
(-5) mod 12 = 12 - 5 = 7
负数在计算机中的存储形式:
负数的补码等于它的反码加1,即在其反码的最低位加1就为该数的补码,且在计算机中负数以补码形式进⾏存储。
已知:    1、int型占4字节(32位⼆进制)char型占1字节(8位⼆进制)
2、字符在内存中以ASCII形式存储(A的为65,C为67)
3、在内存中低地址存低位,⾼地址存⾼位
⼆、具体内容
先规定⼀个int型负数int i= - 48829;
原码为:1 000 0000 / 0000 0000/1011 1110/1011 1101
反码为:1 111 1111/ 1111 1111/0100 0001/0100 0010
补码为:1 111 1111/ 1111 1111/0100 0001/0100 0011
即可假设该数在内存中的实际存放为:
低地址位,地址值为&i      0100 0011
0100 0001
1111 1111
⾼地址位,地址值为&i+3    1111 1111
然后⽤char型指针p1和p2分别指向地址&i和&i+1,并进⾏输出,分别得到p1输出字母C,p2输出字母A,即说明了&i地址中的内容为0100 0011,&i+1中的内容为0100 0001
即验证了是以补码形式存储,⽽不是原码或反码!
三、分析总结
四、实例测试代码
#include
int main(void)
{inti;char *p1;char *p2;
i= -48829; //假设负数存储形式为反码,即为: 1111 1111/ 1111 1111/0100 0001/0100 0011 p1 = &i; //假设p1指向 0100 0011 (67)
p2 = p1 + 1;//假设p2指向 0100 0001 (65)
printf("%c", *p1); //输出字符C(67),得证
printf("%c", *p2); //输出字符A(65),得证
getchar();return 0;
}
View Code
FLOAT 以及DOUBLE的存储形式:
|--浮点数怎么存储在计算机中
浮点型变量是由符号位+阶码位+尾数位组成。
float型数据 ⼆进制为32位,符号位1位,阶码8位,尾数23位
double型数据 ⼆进制为64位,符号位1位,阶码11位,尾数52位
|--单精度32位存储
1bit 8bit 23bit
|--双精度64位存储
1bit 11bit 52bit
浮点数⼆进制存储形式,是符号位+阶码位+尾数位(针对有符号数)
浮点数没有⽆符号数(c语⾔)
|--阶码:
这⾥阶码采⽤移码表⽰,对于float型数据其规定偏置量为127,阶码有正有负,
对于8位⼆进制,则其表⽰范围为-128-127,double型规定为1023,其表⽰范围为-1024-1023
⽐如对于float型数据,若阶码的真实值为2,则加上127后为129,其阶码表⽰形式为10000010
|--尾数:
有效数字位,即部分⼆进制位(⼩数点后⾯的⼆进制位),
因为规定M的整数部分恒为1(有效数字位从左边不是0的第⼀位算起),所以这个1就不进⾏存储
|--具体步骤:
把浮点数先化为科学计数法表⽰形式,eg:1.1111011*2^6,然后取阶码(6)的值加上127(对于float)
计算出阶码,尾数是处⼩数点后的位数(1111011),如果不够23位,则在后⾯补0⾄23位。
最后,符号位+阶码位+尾数位就是其内存中⼆进制的存储形式
1eg:2 #include
3 #include
4 int main(int argc, char *argv[])5{6 int x = 12;7 char *q = (char *)&x;8 float a=125.5;9 char *p=(char *)&a;10 11 printf("%d", *q);12
13 printf("%d",*p);14 printf("%d",*(p+1));15 printf("%d",*(p+2));16 printf("%d",*(p+3));17 return 0;18}19
20output:21 12
22 0
23 0
24 -5
25 66
View Code
|--对于float型:
125.5⼆进制表⽰为1111101.1,由于规定尾数的整数部分恒为1,
则表⽰为1.1111011*2^6,阶码为6,加上127为133,则表⽰为10000101
⽽对于尾数将整数部分1去掉,为1111011,在其后⾯补0使其位数达到23位,
则为11110110000000000000000
内存中的表现形式为:
00000000 低地址
00000000
11111011
01000010 ⾼地址
存储形式为: 00 00 fb 42
依次打印为: 0 0 -5 66
解释下-5,内存中是:11111011,因为是有符号变量所以符号位为1是负数,
所以其真值为符号位不变取反加⼀,变为:10000101化为⼗进制为-5.
# include
intmain()
{int a=-5;
printf("a=-5: %x", a);return 0;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。