Linux 0.12 OS. math - add.c

来源:互联网 发布:电子数据交换技术ppt 编辑:程序博客网 时间:2024/06/04 19:52

临时实数的加法运算模拟了整数的加法运算,把符号位和尾数部分整合成一个signed整数,在利用add指令完成加法操作。

一 正数与负数

这里说一下我自己理解的另一种补码的表现方式,以下用一个byte来做解释,先来举个例子

5的补码:0b00000101,它对应的无符号值也是为5

-5的补码:0b00000101 -> 取反-> 0b11111010 -> +1 ->0b11111011,它对应的无符号值是251

细心的你应该早已发现,-5的补码其实就是256-5的无符号值,8位二进制可以表示256种数值,也即8位的无符号值可以对应出8位带符号的值

0 -> 0

1 -> 1

...

127 -> 127

128 -> -128

129 -> -127

...

255 -> -1

可以发现负数的补码其实是256-(-负数)的二进制形式。

计算机课程中,-5的补码形式是将原码的符号位不变,数值位取反,然后加1,我们将256-(-负数)变化一下就是255-(-负数)+1,有没有感觉,255-(-负数)其实就是数值位取反。还有一个非常妙的地方,251(-5的无符号数值)的补码是什么,我们来计算一下,256-(-5)=5,也就是说5的正数和负数的补码运算是可逆的。

二 求补码

/* 求补码,正数的补码为本身,负数的补码等于其原码的符号位不变,数值部分的各位取反,然后整个数加1。*/#define NEGINT(a) \__asm__("notl %0 ; notl %1 ; addl $1,%0 ; adcl $0,%1" \:"=r" (a->a),"=r" (a->b) \:"0" (a->a),"1" (a->b))

三 符号化

static void signify(temp_real * a){/* 增加2位防止溢出,一位用来表示符号,一位用来防止溢出 */a->exponent += 2; /* 如果指数已经达到最大值怎么办?这样直接加会导致溢出到符号位,应该这样  if ((a->exponent & 0x7fff) < 0x7ffe)  a->exponent += 2;*/__asm__("shrdl $2,%1,%0 ; shrl $2,%1":"=r" (a->a),"=r" (a->b):"0" (a->a),"1" (a->b)); /* 指数+2,对应的尾数向右移动2位 */if (a->exponent < 0) /* 判断符号位负数(最高位为1)*/NEGINT(a); /* 求a(负数)的补码 */a->exponent &= 0x7fff;}static void unsignify(temp_real * a){if (!(a->a || a->b)) { /* zero */a->exponent = 0;return;}a->exponent &= 0x7fff;if (a->b < 0) { /* 最高位为1,为负数 */NEGINT(a);a->exponent |= 0x8000;}while (a->b >= 0) { /* 规范化,指数部分不能<0?*//* while ((a->exponent & 0x7fff) && a->b >=0) */a->exponent--;__asm__("addl %0,%0 ; adcl %1,%1":"=r" (a->a),"=r" (a->b):"0" (a->a),"1" (a->b));}}

四 加法

void fadd(const temp_real * src1, const temp_real * src2, temp_real * result){temp_real a,b;int x1,x2,shift;x1 = src1->exponent & 0x7fff;x2 = src2->exponent & 0x7fff;if (x1 > x2) {a = *src1;b = *src2;shift = x1-x2;} else {a = *src2;b = *src1;shift = x2-x1;}if (shift >= 64) {*result = a;return;}/*当 x1 =x2时,b的64个有效bit需要向右移动shift位数,若shift超过了64,那么b = 0,因此a + b = a + 0 = a。*/if (shift >= 32) {b.a = b.b;b.b = 0;shift -= 32;}/*若shift超过了32,那么b的后32bit尾数会被抹掉。经过两轮条件后,保证了shift<32。*/__asm__("shrdl %4,%1,%0 ; shrl %4,%1":"=r" (b.a),"=r" (b.b):"0" (b.a),"1" (b.b),"c" ((char) shift));/*shrd指令:双精度右移指令,只能在80386以上处理器使用,将目的操作数向右移动相应位数,空出来的位由源操作数的低位填充。EX: shrd $10,%eax,%ebx将ebx向右移动10位,ebx的高10位由eax的低10位补充。到这里后,a和b的指数已经是一样的了,下面就开始把它们符号化模拟正常数的加法。*/signify(&a); /* 加上符号 */signify(&b);__asm__("addl %4,%0 ; adcl %5,%1":"=r" (a.a),"=r" (a.b):"0" (a.a),"1" (a.b),"g" (b.a),"g" (b.b));unsignify(&a); /* 去掉符号 */*result = a;}

原创粉丝点击