《Intro to Computer Systems》(15-213)LAB1(Data lab)

这篇博客探讨了C语言中位操作用于实现浮点数的处理,包括指数和尾数的表示,以及如何进行浮点数的乘法、转换和比较。文章详细解释了不同位操作的用途,如取反、按位与、按位或和右移,并提供了示例代码来展示如何实现特定数学和逻辑操作,如求最小值、判断所有奇数位、取负和判断是否小于等于。同时,还讨论了浮点数在计算机中的表示,以及如何处理溢出和精度问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

课程首页

信息的表示和处理

浮点数

在这里插入图片描述

在规格化数中,需要加入bias偏置项,需要注意的是,e全0(非规格化数)与e为0x01表示的指数值相同,由于尾数自动从0.变成了1.0,从而实现了平稳过渡。从lab1中的实验可以进行验证。

注意

  1. 由于浮点数由指数和尾数两部分固定长度组成,随着数字变大,表示的精度会变小。如下图
    在这里插入图片描述

  2. 有符号数右移时,符号位如果是1,则会进行补1。

  3. 负数的一种计算方式是-x = (~x) + 1,因此有-INT_MIN = INT_MIN。

  4. 补补得原。

  5. 负数的补码, -3 = 1111 1101 = 111 1101 = 1101,这个性质后面会用到。

  6. 以-3 = 1111 1101为例,负数的补码也可以看成-28+27+26+25+24+23+22+20

lab1

/* 
 * bitXor - x^y using only ~ and & 
 *   Example: bitXor(4, 5) = 1
 *   Legal ops: ~ &
 *   Max ops: 14
 *   Rating: 1
 */
int bitXor(int x, int y) {
    return !((~0) ^  (x + 1));
    //return (~(x & y)) & (~((~x) & (~y)));
}

本来a^b = (a&b) | ((~a)&(~b))a|b = ~((~a)&(~b)可以得到。

/* 
 * tmin - return minimum two's complement integer 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmin(void) {
    return 1 << 31;
}

int32位的最小值为 0x80000000

int isTmax(int x) {
    //a == b ->  !(a^b)
    return !((~0) ^ (x ^ (x + 1))) & !!(x+1);
}

如果是可以验证发现,只有x = 0X7FFFFFFF或者x=-1时!((~0) ^ (x ^ (x + 1))) = 1,再通过!!(x+1)拒绝x = -1的情况。

/* 
 * allOddBits - return 1 if all odd-numbered bits in word set to 1
 *   where bits are numbered from 0 (least significant) to 31 (most significant)
 *   Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 2
 */
int allOddBits(int x) {
    int flag = 0xAA;
    flag = (flag << 8) + flag;
    flag = (flag << 16) + flag;
    return !(flag ^ (x & flag));
}

首先使用&运算只保留奇数位置上的数。再判断是否和奇数位全1的数字是否相等。

/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int negate(int x) {
    return (~x) + 1;
}

(~x) + 1本身就是取反的一种实现,比如在-INT_MIN == INT_MIN会返回true

/* 
 * isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters '0' to '9')
 *   Example: isAsciiDigit(0x35) = 1.
 *            isAsciiDigit(0x3a) = 0.
 *            isAsciiDigit(0x05) = 0.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 3
 */
int isAsciiDigit(int x) {
    int ans1 = !((x + (~0x30) + 1) >> 31);
    int ans2 = ((x + (~0x3A) + 1) >> 31);
    return ans1 & ans2;
}

通过x-0x30x-0x3A的符号进行判断,关于数字溢出,可以通过区间判断得出溢出时也会返回正确结果。

/* 
 * conditional - same as x ? y : z 
 *   Example: conditional(2,4,5) = 4
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 16
 *   Rating: 3
 */
int conditional(int x, int y, int z) {
    int mask = (!x) + (~1) + 1;
    return (mask & y) | (~mask & z);
}

巧妙的使用mask,当x为真时,mask 为全1,当x为假时mask为全0。

/* 
 * isLessOrEqual - if x <= y  then return 1, else return 0 
 *   Example: isLessOrEqual(4,5) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 24
 *   Rating: 3
 */
int isLessOrEqual(int x, int y) {
    int signx = (x >> 31) & 1;
    int signy = (y >> 31) & 1;
    int isSameSign = !(signx ^ signy);
    int sub = (y + (~x) + 1) >> 31;
    return (isSameSign & (!sub)) | ((!isSameSign) & signx);
}

简单做差判符号会有不同号溢出问题,因此添加一个同号和非同号的判断。

/* 
 * logicalNeg - implement the ! operator, using all of 
 *              the legal operators except !
 *   Examples: logicalNeg(3) = 0, logicalNeg(0) = 1
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4 
 */
int logicalNeg(int x) {
    return ((x | (~x + 1)) >> 31) + 1;
}

技巧性比较强,参考http://zhouni.net/questions/a19511558888145.html。

/* howManyBits - return the minimum number of bits required to represent x in
 *             two's complement
 *  Examples: howManyBits(12) = 5
 *            howManyBits(298) = 10
 *            howManyBits(-5) = 4
 *            howManyBits(0)  = 1
 *            howManyBits(-1) = 1
 *            howManyBits(0x80000000) = 32
 *  Legal ops: ! ~ & ^ | + << >>
 *  Max ops: 90
 *  Rating: 4
 */
int howManyBits(int x) {
  int b16,b8,b4,b2,b1,b0;
  int sign=x>>31;
  x = (sign&~x)|(~sign&x);//如果x为正则不变,否则按位取反(这样好找最高位为1的,原来是最高位为0的,这样也将符号位去掉了)


// 不断缩小范围
  b16 = !!(x>>16)<<4;//高十六位是否有1
  x = x>>b16;//如果有(至少需要16位),则将原数右移16位
  b8 = !!(x>>8)<<3;//剩余位高8位是否有1
  x = x>>b8;//如果有(至少需要16+8=24位),则右移8位
  b4 = !!(x>>4)<<2;//同理
  x = x>>b4;
  b2 = !!(x>>2)<<1;
  x = x>>b2;
  b1 = !!(x>>1);
  x = x>>b1;
  b0 = x;
  return b16+b8+b4+b2+b1+b0+1;//+1表示加上符号位
}

此题需要对负数补码的性质有一个清晰的理解。参考CSAPP 之 DataLab详解,没有比这更详细的了

//float
/* 
 * floatScale2 - Return bit-level equivalent of expression 2*f for
 *   floating point argument f.
 *   Both the argument and result are passed as unsigned int's, but
 *   they are to be interpreted as the bit-level representation of
 *   single-precision floating point values.
 *   When argument is NaN, return argument
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
unsigned floatScale2(unsigned uf) {
    if ((uf & 0x7F800000) == 0x7F800000)
        return uf;
    if (uf & 0x7F800000) {  //e != 0
        uf = uf + 0x00800000;
    } else {
        return ((uf & 0x007FFFFF) << 1) | (uf & 0x80000000);
    }
    return uf;
}

对阶符全0进行特判,在判断阶符是否全为0,如果全为0,只需要右移尾数,不全为0,则使阶符号+1。

/* 
 * floatFloat2Int - Return bit-level equivalent of expression (int) f
 *   for floating point argument f.
 *   Argument is passed as unsigned int, but
 *   it is to be interpreted as the bit-level representation of a
 *   single-precision floating point value.
 *   Anything out of range (including NaN and infinity) should return
 *   0x80000000u.
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
int floatFloat2Int(unsigned uf) {
    int e = uf & 0x7F800000;
    int frac = uf & 0x007FFFFF;
    int n = (e >> 23) - 127;
    if (n < 0) return 0;
    if (n >= 31) return 0x80000000u;
    frac = frac + 0x00800000;   //1.xxx
    if (n <= 23)
        frac = (frac >> (23 - n));
    else
        frac = (frac << (n - 23));
    if (uf & 0x80000000)
        return -frac;
    else
        return frac;
}
/*

根据题意,先对阶数进行下溢和上溢特判,之后得到完整的尾数ans = frac + 0x00800000,再根据阶数判断完整的尾数是左移,还是右移处理。

/* 
 * floatPower2 - Return bit-level equivalent of the expression 2.0^x
 *   (2.0 raised to the power x) for any 32-bit integer x.
 *
 *   The unsigned value that is returned should have the identical bit
 *   representation as the single-precision floating-point number 2.0^x.
 *   If the result is too small to be represented as a denorm, return
 *   0. If too large, return +INF.
 * 
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while 
 *   Max ops: 30 
 *   Rating: 4
 */
#include<stdio.h>
unsigned floatPower2(int x) {
    if (x > 127) return 0x7F800000;
    if (x >= 0) return (x << 23) + 0x3F800000;
    //x < 0
    if (x < -(23 + 126)) return 0x0;
    if (x <= -126) return 1 << ((-x) - 126);
    else return ((-x) + 127) << 23;
}

这题难度不大,坑就坑在这题样例非常多,默认10秒机子性能不好的话会超时。太坑了
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值