2108323759.png
2108323759.png

A. 通过展开每个十六进制转换为二进制:

2 5 B 9 D 2

0010 0101 1011 1001 1101 0010

b. 先分为4位一组, 不足四位的, 前边补零:> 1010 1110 0100 1001

A E 4 9

c. 同 A

A 8 B 3 D

1010 1000 1011 0011 1101

d. 同b

0011 0010 0010 1101 1001 0110

3 2 2 D 9 5

3202481767.png
3202481767.png

仅关注hex, 则有 23=3+4*5=0x800000,

2^15=32768, 15=3+4*2=0x800,

0x2000=1+4*3=2^13,

12=4*3+0=0x1000,

64=2^6=4*1+2=0x20,

0x100=64

157235339.png
157235339.png

十进制与十六进制之间的转换相对更容易些, 如下:

158=16*9 + 14 = 0x9E = 1001 1110

76=16*4 + 12 = 0x4C = 0100 1100

145=16*9 + 1 = 0x91 = 1001 0001

1010 1110 = 0xAE = 16 * 10 + 14 = 174

0011 1100 = 0x3C = 16*3 + 12 = 60

1111 0001 = 0xF1 = 16*15 + 1 = 241

2248763550.png
2248763550.png

在某个数大于15时才发生进位. 如果减完小于0时, 则变成F即可: A. 0x6061 B. 0x603c C. 0x608e D. 0x009e

5707420382.png
5707420382.png

小端即尾部在前, 大端即头部在前

A. 78 | 12

B. 78 56 | 12 34

C. 78 56 34 | 12 34 56

706349304.png
706349304.png

A. 0027C8F8 -> 0010 0111 1100 1000 1111 1000

4A1F23E0 -> 0100 1010 0001 1111 0010 0011 1110 0000

B.0 0111 1100 1000 1111 1000 共21位能匹配上

C. 整形表示的前几位以及浮点的前后几位对不上

1998488456.png
1998488456.png

由于字符编码方式是固定从前到后, 且在尾部填充0x00, 所以打印的结果为: 6D 6E 6F 70 71 72

9585716940.png
9585716940.png

~a 10110001

~b 00011110

a&b 01000000

a|b 11101111

a^b 10010000

637424589.png
637424589.png

A. Black <> White

Blue <> Yellow

Green <> Magenta

Cyan <> Red

B. 001 | 010 = 011 = Cyan

110 & 011 = 010 = Green

100 ^ 101 = 001 = Blue

1520766867.png
1520766867.png

Step 1 : a a^b

Step 2 : 0^b=b a^b

Step 3 : b a

582882909.png
582882909.png

A. 循环到结尾时, 头指针与尾指针是指向同一个元素的, 就是k;

B. 因为inplace_swap第一步就是对输入的两个参数进行异或,自身与自身异或后就变成0;

C. 将first<=last改为first<last即可避免该问题。

6815917291.png
6815917291.png

A. x & 0xFF

B. x ^ ~0xFF

C. x | 0xFF

6011234190.png
6011234190.png

首先写出bis与bic的C语言形式如下:

bis -> x | m

bic -> x & ~m

现在要求x | y 则直接调用bis即可 bis(x, y)

要求 x ^ y 等效于找到一个掩码m, 这个码在x与y相同的位上为1,不同的位上为0,然后用bis和bic配合达到异或效果。

某一位上 xy 同时为1, 则bis为1, bic为0; xy同时为0则bis为0, bic为0; x为1 y为0则bis为1, bic为1, x为0 y为1则bis为1, bic为0

调用两次bic再对结果bis即可:

bis(bic(x,y), bic(y,x))

4978645098.png
4978645098.png

0x55 = 0101 0101 0x46 = 0100 0110

~0x55 = 1010 1010 ~0x46 = 1011 1001

a&b = 0100 0100 a&&b = true

a|b = 0101 0111 a||b = true

~a|~b = 1011 1011 !a||!b = false

a&!b = 0001 0001 a&&~b = false

287266995.png
287266995.png

思路:通过异或可以实现在x等于y时结果为0,其余情况下均不为0,然后取反即可。

!(x ^ y)

4526913297.png
4526913297.png

0xD4 = 1101 0100 <<2 0101 0000 >>>3 0001 1010 >>3 1111 1010

0x64 = 0110 0100 <<2 1001 0000 >>>3 0000 1100 >>3 0000 1100

0x72 = 0111 0010 <<2 1100 1000 >>>3 0000 1110 >>3 0000 1110

0x44 = 0100 0100 <<2 0001 0000 >>>3 0000 1000 >>3 0000 1000

2805272598.png
2805272598.png

0x1 = 0001 = 2^0 = 1 = 1

0xB = 1011 = 2^3 + 2^1 + 2^0 = 11 = -5

0x2 = 0010 = 2^1 = 2 = 2

0x7 = 0111 = 2^2 + 2^1 + 2^0 = 7 = 7

0xC = 1100 = 2^3 + 2^2 = 12 = -4

6846633068.png
6846633068.png

32位补码形式表示的十六进制 -> 等价的十进制

此题意思不明确,跳过。

7500749156.png
7500749156.png

T2U表示将有符号数转化为无符号数:

-1 1111 > 15

-5 1011 > 11

-6 1010 > 10

-4 1010 > 12

1 0001 > 1

8 有bug,无法表示

276461915.png
276461915.png

答: 对于-1、-5、-6与-4,由于是负数需要加上2^4=16. 对于1保持不变即可。

953430086.png
953430086.png

无符号 0

有符号 1

无符号 0

有符号 1

无符号 1

9159590096.png
9159590096.png

A. 套用B2T的公式得到 -1 * 2^3 + 2 + 1 = -5

B. 根据符号扩展可得 11011=1011

C. 同B.

3966727120.png
3966727120.png

A. word是无符号型, 所以直接对word做移位都是逻辑移位. 同时unsigned是32位的, 相当于8个十六进制数字, 移24位相当于6个十六进制.

强制类型转换的优先级要高于移位的优先级. 强制类型转换会保持位模式不变, 但是移位后变成算数移位.

0x00000076 0x00000076

0x00000021 0x00000021

0x000000C9 0xFFFFFFC9

0x00000087 0xFFFFFF87

B.

func1 转为int型并保留最后两位数字

func2 在最后两位小于0x80时取原始值;大于等于0x80时取补码值;?

6160122692.png
6160122692.png

无符号数截断直接移除首位即可:

0001 = 001 = 1

0011 = 011 = 3

1100 = 100 = 4

1110 = 110 = 6

有符号数也是移除首位, 不过按照有符号来计算实际值:

1

3

-4

-2

9825510943.png
9825510943.png

在表达式中同时包含有符号和无符号数时, 会将所有数字都转为无符号数再进行计算. 因此

length - 1 变为 0U - 1U (这是无符号运算) 值为 2^32 -1. 导致 result += a[1]会被执行, 出现错误. 可以修改为:

for (i=0; i < length; i++)

4608842009.png
4608842009.png

A. 在s的长度小于t的长度时会发生此种情况.

B. 因为无符号数减法, 如果s<t则会出现溢出, 导致结果是一个很大的正数.

C> 改为strlen(s) > strlen(t)即可

2015088830.png
2015088830.png

int uadd_ok(unsigned x, unsigned y){
    if (x + y >= x) {
        return 1;
    } else {
        return 0;
    }
}

3229462938.png
3229462938.png

1 = 1 > 2^4 - 1 = 15 = F 0001 <> 1111

4 = 4 > 2^4 - 4 = 12 = C

7 = 7 > 2^4 - 7 = 9 = 9

A = 10 > 2^4 - 10 = 6 = 6

E = 14 > 2^4 - 14 = 2 = 2

2 = 2 > 2^4 -2 = 14 = E 0010 <> 1110

9 = 9 > 2^4 -9 = 7 1001 <> 0111

237544328.png
237544328.png

10100 + 10001 ( -12 + -15 = -27 ) == 00101 = 5 > situation 1

11000 + 11000 ( -8 + -8 = -16 ) == 10000 = -16 > situation 2

10111 + 01000 ( -9 + 8 = -1 ) == 11111 = -1 > situation 2

00010 + 00101 ( 2 + 5 = 7 ) == 00111 = 7 > situation 3

01100 + 00100 ( 12 + 4 = 16 ) == 10000 = -16 > situation 4

322864007.png
322864007.png

int tadd_ok(int x, int y){
    if (x<0 && y<0 && x + y >= 0) {
        return 0;
    } else if (x>0 && y>0 && x + y <=0) {
        return 0;
    } else {
        return 1;
    }
}

8142668399.png
8142668399.png

由于补码的加减法在位表示上与无符号数完全相同, 因此先加后减并不会损失信息, 因此该函数的返回结果恒为真.

8365005545.png
8365005545.png

注意到y的取值范围是-2^32 ~ 2^32-1. 当y取-2^32时, 直接-y就必然会产生溢出. 此时x是任意非负值, 都会导致结果不对.

为了修复这个问题, 可以同时判断y-x也不会发生溢出. 因为如果x-y溢出了, 那么y-x肯定也溢出了.

int tsub_ok(int x, int y){
    return tadd_ok(x, -y) && tadd_ok(y, -x);
}

2682184831.png
2682184831.png

2H = 2D -2H = -2D 0010 <> 1110

3H = 3D -3H = -3D

9H = 1001 = -7D -9H = 7D 0111

BH = 1011 = -5D -BH = 5D 0101

CH = 1100 = -4D -CH = 4D 0100

两者的位模式完全相同, 因为他们都要求加和为0. 因此我们可以用unsigned的逆元来求signed的逆元.

3093735950.png
3093735950.png

无符号: x=100B=4D y=101B=5D x*y=20D=10100 截断后为100

补码: x=100B=-4D y=101B=-3D x*y=12D=01100 截断后为100

961283094.png
961283094.png

  1. argue that the case x=0 is handled correctly.

0乘任何数都为0, 一定不可能溢出, 所以x=0时必然返回1, 是正确的.

  1. 证明 x*y=p+t2^w

xy的结果只有两种, 溢出或者不溢出. 溢出时, 结果位数大于w小于等于2w, 将结果拆分为w+1位-2w位和1-w位两部分.分别对应于s和p.s可以看做是一个不超过w位的数移位w位得到, 即s=t2^w. 当且仅当t不为0时, s不为0, xy发生溢出.

  1. 证明p可以写作p=x*q+r

由于p就是x/q的结果, 所以p=x*q加上余数, 余数r的绝对值小于x

  1. 证明当且仅当r=t=0时, q=y

证明if: 如果r=t=0 根据公式可得xy=xq, 又因为x不为0, 所以y=q

证明onlyif: 如果r或者t中任意一个不为0, 则xy=xq+N, N不为0, 此时y=(q+N/x)不等于q.

7731672882.png
7731672882.png

// 如果溢出, 则32~64位必不为0
int tmult_ok(int x, int y) {
    int64_t p = (int64_t)x * y; // 注意此处必需先做强制类型转换, 否则结果会变为溢出之后的结果

    return (int)p == p; // 用移位的话, 负数可能会有问题
}

1232605340.png
1232605340.png

k=0 b=0 1倍 b=a 2倍

k=1 b=0 2倍 b=a 3倍

k=2 b=0 4倍 b=a 5倍

k=3 b=0 8倍 b=a 9倍

8158948903.png
8158948903.png

可以修改为 x<<n + x<<n - x<<m

9996481720.png
9996481720.png

K = 7 移位1次, 由于7=8-1, 所以使用 x<<3 - x即可

K = 30 = 11110, 移位4次, 和有效位数量相同, 因此使用 x<<4 + x<<3 + x<<2 + x<<1即可

K = 28 = 11100, 移位2次, 28=100000 - 100, 因此使用 x<<5 - x<<2即可

K = 55 = 110111, 移位2次, 加减两次. 我们先将K+1可以得到111000, 这个可以用两次移位和一次加减得到即 x<<6-x<<3, 然后再减去加的一次得到 x<<6-x<<3-x即可

4361222901.png
4361222901.png

在n=m时, 使用A. n!=m时, 由于加法和减法的开销相同, 因此优先使用B. 如B会造成溢出, 再使用A.

4230356684.png
4230356684.png

int div16(x)
{
    int symbol_bit = x>>32 & 1;
    return x + (symbol_bit<<4 - symbol_bit) >> 4;
}

523693856.png
523693856.png

将optarith写成数学表达式, 我们可以得到:

x * 32 - x + y/8

所以M=31, N=8

9100579135.png
9100579135.png

A. 2 x=-2^32时, x-1>0 且x<0

B. 1 7 = 111, x<<29为左移不用考虑符号位?

C. 2 x>2^16次方会产生溢出

D. 2 x不为-2^32

E. 这个题目肯定有问题

F. 2 任意两个相加不会溢出的负数, 如-1和-1

G. 2 假设x,y均大于0, 则 x*~y + uy*ux = x*~y + y*x=x*(y+~y), 不成立

3773639964.png
3773639964.png

3/4 = 0.11B = 0.75D

5/16 = 0.0101B = 0.3125D

10.1011 = 2 + 1/2 + 1/8 + 1/16 = 43/16 = 2.6875D

1.001 = 1 + 1/8 = 9/8 = 1.125D

5.875 = 5875/1000 = 47/8 = 101.111B

3.1875 = 31875/10000 = 255/80 = 51/16 = 11.0011B

3155652617.png
3155652617.png

A. 0.000000000000000000000[0011]...

B. x = 3/32 + 3/512 + 3/8192 + 3/131072 + 3/2097152 = 0.099999904632 0.1-x = 0.000000093 = 9*10^-8

C. 100h = 100 3600s = 0.3610^6, 100h 910^-8 = 3.24*10^-2

D, 3.2410^-2 2000 = 62.8m

4152430903.png
4152430903.png

在阶码为全0时, 阶码偏置后为1-Bias,这里Bias为1所以偏置后为0, 此时f没有隐含的1就是原始小数:

0 00 00 e=0 E=0 2^E=1 f=0 M=0 2^E*M = 0 V=+0 Dec=0

0 00 01 e=0 E=0 2^E=1 f=1/4 M=1/4 2^E*M=1/4 V=1/4 Dec=0.25

0 00 10 e=0 E=0 2^E=1 f=2/4 M=2/4 2^E*M=1/4 V=1/2 Dec=0.5

0 00 10 e=0 E=0 2^E=1 f=3/4 M=3/4 2^E*M=3/4 V=3/4 Dec=0.75

0 01 00 e=1 E=0 2^E=1 f=4/4 M=4/4 2^E*M=4/4 V=1 Dec=1

9611106051.png
9611106051.png

3510593 = 11 0101 1001 0001 0100 0001B

32位包括1个符号位,8个阶码位与23个小数位。

阶码偏置为127,E=e-127. 由于3510593一共是22位,刨除一个隐含位,剩余21位,需要左移21位可以得到阶码为148,转化为二进制为10010100.

因此全部位为

0100 1010 0101 0110 0100 0101 0000 0100 即4A564504

4465391428.png
4465391428.png

A.不能准确描述的最小正整数 - 相当于无论如何左移或者右移均达不到要求。考虑到隐含位,此时该整数的最高位一定是1.同时,如果该整数是在n+1位以内, 那么都可以表示。包括首位为0余下都为1的情形,这时只需要在尾部留一个0然后控制阶数即可。因此该整数一定是n+2位,首位为1,且末位不为0,满足该条件最小的数为2^(n+1)+1

B.2^24+1=16777217

8852389902.png
8852389902.png

A. 不是在中间,所以舍入为11.0

B. 在中间, 舍入偶数为11.0

C. 不在中间, 舍入为11.0

D. 在中间,舍入为11.0

8469934964.png
8469934964.png

A. 0.0001 1001 1001 1001 1001 [1001] 由于尾部为110>100, 所以舍入后为 0.0001 1001 1001 1001 1001 101

B. 约为0.0000002384185791015625=2.384*10^-8

C. 约为8.58*10^-3

D. 约为1.72m

1246884346.png
1246884346.png

101 1110 > 2^2 * 1.111=111.1=15/2 由于有4位小数,需要舍入,舍入后为1000 = 2^3, 3+7=10所以E=1010, 1010 000

010 1001 > 1/2 * 1.1001=0.11001=1/2+1/4+1/32=25/32, 由于有5位小数,需要舍入,舍入后为0.1100, 移位-1,-1+7=6所以E=0110, 0110 100

110 1111 > 2^3 * 1.1111=1111.1=31/2, 由于有5位小数, 需要舍入, 舍入后为10000, 移位4, 4+7=13所以E=1101, 1101 000

000 0001 > 2^-2 * 0.0001 = 0.000001=1/64, 用非规则形式, E=1-7=-6, 无法表示。用规则形式,隐含位移-6位,-6+7=1所以E=0001, 0001 000

1488352469.png
1488352469.png

define POS_INFINITY (double)1.8*10^109

define NEG_INFINITY -(double)1.8*10^109

~#define NEG_ZERO -(double)0~ -1.0/POS_INFINITY

2414800152.png
2414800152.png

A. 总是为真。 因为int转double能保留精确值,double转int在范围内

B. 不总是为真,可能发生舍入。如2^24+1就会有问题。

C. 不总是为真,同上;

D. ~不总是为真,同上;~ 同A.

E. ~不总是为真, f=-2^32时,-f=0, -0仍然为0.~ float型只需要改变符号位即可