现在的位置: 首页 > 综合 > 正文

Linux 0.12 OS. math – add.c

2013年10月02日 ⁄ 综合 ⁄ 共 2128字 ⁄ 字号 评论关闭

临时实数的加法运算模拟了整数的加法运算,把符号位和尾数部分整合成一个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;
}

抱歉!评论已关闭.