初始完成日期:2017.9.26
許可:除2.55對應代碼外(如需使用請聯系 randy.bryant@cs.cmu.edu),任何人可以自由的使用,修改,分發本文檔的代碼。
本機環境: (有一些需要在多種機器上測試的就沒有試驗)
frank@under:~/tmp$ uname -a
Linux under 4.10.0-35-generic #39~16.04.1-Ubuntu SMP Wed Sep 13 09:02:42 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
2.55
/*這一段代碼的大部分來自http://csapp.cs.cmu.edu/3e/students.html*/
/* $begin show-bytes */
#include <stdio.h>
/* $end show-bytes */
#include <stdlib.h>
#include <string.h>
/* $begin show-bytes */
typedef unsigned char *byte_pointer;
void show_bytes(byte_pointer start, size_t len) {
size_t i;
for (i = 0; i < len; i++)
printf(" %.2x", start[i]); //line:data:show_bytes_printf
printf("\n");
}
void show_int(int x) {
show_bytes((byte_pointer) &x, sizeof(int)); //line:data:show_bytes_amp1
}
void show_float(float x) {
show_bytes((byte_pointer) &x, sizeof(float)); //line:data:show_bytes_amp2
}
void show_pointer(void *x) {
show_bytes((byte_pointer) &x, sizeof(void *)); //line:data:show_bytes_amp3
}
/* $end show-bytes */
/* $begin test-show-bytes */
void test_show_bytes(int val) {
int ival = val;
float fval = (float) ival;
int *pval = &ival;
show_int(ival);
show_float(fval);
show_pointer(pval);
}
/* $end test-show-bytes */
void simple_show_a() {
/* $begin simple-show-a */
int val = 0x87654321;
byte_pointer valp = (byte_pointer) &val;
show_bytes(valp, 1); /* A. */
show_bytes(valp, 2); /* B. */
show_bytes(valp, 3); /* C. */
/* $end simple-show-a */
}
void simple_show_b() {
/* $begin simple-show-b */
int val = 0x12345678;
byte_pointer valp = (byte_pointer) &val;
show_bytes(valp, 1); /* A. */
show_bytes(valp, 2); /* B. */
show_bytes(valp, 3); /* C. */
/* $end simple-show-b */
}
void float_eg() {
int x = 3490593;
float f = (float) x;
printf("For x = %d\n", x);
show_int(x);
show_float(f);
x = 3510593;
f = (float) x;
printf("For x = %d\n", x);
show_int(x);
show_float(f);
}
void string_ueg() {
/* $begin show-ustring */
const char *s = "ABCDEF";
show_bytes((byte_pointer) s, strlen(s));
/* $end show-ustring */
}
void string_leg() {
/* $begin show-lstring */
const char *s = "abcdef";
show_bytes((byte_pointer) s, strlen(s));
/* $end show-lstring */
}
void show_twocomp()
{
/* $begin show-twocomp */
short x = 12345;
short mx = -x;
show_bytes((byte_pointer) &x, sizeof(short));
show_bytes((byte_pointer) &mx, sizeof(short));
/* $end show-twocomp */
}
int main(int argc, char *argv[])
{
int val = 12345;
if (argc > 1) {
if (argc > 1) {
val = strtol(argv[1], NULL, 0);
}
printf("calling test_show_bytes\n");
test_show_bytes(val);
} else {
printf("calling show_twocomp\n");
show_twocomp();
printf("Calling simple_show_a\n");
simple_show_a();
printf("Calling simple_show_b\n");
simple_show_b();
printf("Calling float_eg\n");
float_eg();
printf("Calling string_ueg\n");
string_ueg();
printf("Calling string_leg\n");
string_leg();
}
return 0;
}
編譯運行輸出:
frank@under:~/tmp$ gcc 255.c && ./a.out
calling show_twocomp
39 30
c7 cf
Calling simple_show_a
21
21 43
21 43 65
Calling simple_show_b
78
78 56
78 56 34
Calling float_eg
For x = 3490593
21 43 35 00
84 0c 55 4a
For x = 3510593
41 91 35 00
04 45 56 4a
Calling string_ueg
41 42 43 44 45 46
Calling string_leg
61 62 63 64 65 66
數據的低位放置在低地址處,字符串按照順序從低位地址排列。由此可知該機器為小端字節排序。
2.56
show-bytes代碼同2.55。
frank@under:~/tmp$ gcc 255.c && ./a.out 192837465
calling test_show_bytes
59 77 7e 0b
76 e7 37 4d
28 00 4d 93 fc 7f 00 00
十進制192837465二進制表示為:1011011111100111011101011001。
-
十六進制表示為:0xB7E7759,所以第一行的數據表示這是小端字節排序。
-
將二進制小數點左移二十七位,由於單精度浮點數尾數部分只有23位(IEEE 756),多出來的4位1001將丟棄,由於默認的“round to even”,高位將進一位,即1.01101111110011101110110*2^27,bias = 2^7 - 1 = 127,所以階碼部分應為127+27=0x9A,整體為:0,10011010,01101111110011101110110即0x4D37E776。由此看出為小端字節排序。
-
該行為小端字節顯示的指針。
2.57
#include <stdio.h>
typedef unsigned char *byte_pointer;
void show_short(void);
void show_long(void);
void show_double(void);
void show_bytes(byte_pointer start, size_t len);
int main(int argc, char const *argv[])
{
show_short();
show_long();
show_double();
return 0;
}
void show_bytes(byte_pointer start, size_t len) {
size_t i;
for (i = 0; i < len; i++)
printf(" %.2x", start[i]);
printf("\n");
}
void show_short(void)
{
short i = 12345;
printf("short i = 12345\n");
show_bytes((byte_pointer)&i, sizeof i);
}
void show_long(void)
{
long i = 123456789;
printf("long i = 123456789\n");
show_bytes((byte_pointer)&i, sizeof i);
}
void show_double(void)
{
double i = 123456789.0;
printf("double i = 123456789.0\n");
show_bytes((byte_pointer)&i, sizeof i);
}
編譯運行輸出:
frank@under:~/tmp$ ./a.out
short i = 12345
39 30
long i = 123456789
15 cd 5b 07 00 00 00 00
double i = 123456789.0
00 00 00 54 34 6f 9d 41
2.58
#include <stdio.h>
#include <stdint.h>
int is_little_endian(void);
int main(int argc, char const *argv[])
{
return is_little_endian();
}
int is_little_endian(void)
{
int32_t i = 1;
unsigned char *p = (unsigned char *)&i;
if(*p)
{
return 1;
}
return 0;
}
編譯運行輸出:
frank@under:~/tmp$ gcc 258.c && ./a.out ; echo $?
1
2.59
#include <stdio.h>
int main(int argc, char const *argv[])
{
int x = 0x89ABCDEF;
int y = 0x76543210;
printf("0x%.8X\n", x&0xFF | y&~0xFF);
return 0;
}
編譯運行輸出:
frank@under:~/tmp$ gcc 259.c && ./a.out
0x765432EF
2.60
#include <stdio.h>
unsigned replace_byte(unsigned x, int i, unsigned char b);
int main(int argc, char const *argv[])
{
printf("%#.8x\n", replace_byte(0x12345678, 2, 0xAB));
printf("%#.8x\n", replace_byte(0x12345678, 0, 0xAB));
return 0;
}
unsigned replace_byte(unsigned x, int i, unsigned char b)
{
int move = i * 8;
return x & ~(0xFF << move) | b << move;
}
編譯運行輸出:
frank@under:~/tmp$ gcc 260.c && ./a.out
0x12ab5678
0x123456ab
2.61
#include <stdio.h>
int main(int argc, char const *argv[])
{
int x, y; /* y means 0 should be returned */
int sizeof_int = sizeof(int);
/*condition A*/
x = ~0;
y = 0xFFFFFF00;
printf("%d\t%d\n", !(~x), !(~y));
/*condition B*/
x = 0;
y = 0x000000FF;
printf("%d\t%d\n", !x, !y);
/*condition C*/
x = 0x000000FF;
y = 0x0000000F;
printf("%d\t%d\n", !((x ^ 0xFF)<<((sizeof_int-1)<<3)), !((y ^ 0xFF)<<((sizeof_int-1)<<3)));
/*condition D*/
x = 0x00FFFFFF;
y = 0x0FFFFFFF;
printf("%d\t%d\n", !(x >> ((sizeof_int-1) << 3)), !(y >> ((sizeof_int-1) << 3)));
return 0;
}
編譯運行輸出:
frank@under:~/tmp$ gcc 261.c && ./a.out
1 0
1 0
1 0
1 0
2.62
#include <stdio.h>
int int_shifts_are_arithmetic(void);
int int_shifts_are_logic(void);
int main(int argc, char const *argv[])
{
printf("%d\n", int_shifts_are_arithmetic());
printf("%d\n", int_shifts_are_logic());
return 0;
}
int int_shifts_are_arithmetic(void)
{
int x = ~0;
return x >> 1 == x;
}
int int_shifts_are_logic(void)
{
unsigned x = ~0;
return x >> 1 == x;
}
我這里由於沒有不同字長/不同機器,就暫時用unsigned 代替了一下邏輯右移。
編譯運行輸出:
frank@under:~/tmp$ ./a.out
1
0
2.63
#include <stdio.h>
unsigned srl(unsigned x, int k);
int sra(int x, int k);
int main(int argc, char const *argv[])
{
printf("%#.8x\n", srl(0x80000000, 8));
printf("%#.8x\n", sra(0x80000000, 8));
return 0;
}
unsigned srl(unsigned x, int k)
{
/* Perform shift arithmetically */
unsigned xsra = (int) x >> k;
/*思路是由k形成諸如0x00FFFFFF這樣的掩碼,與xsra進行與操作從而將高位置零*/
unsigned w = sizeof(int) << 3;
unsigned mask = ~(((1 << k)-1)<<(w-k));
/*(1 << k)-1能夠獲得低位連續為1,高位為0的掩碼,但是其不能達到全1,於是繼續向左移w-k然后取反*/
return mask & xsra;
}
int sra(int x, int k)
{
/* Perform shift logically */
int xsrl = (unsigned) x >> k;
/*這個題目的關鍵點是判斷符號位是否為1,通過test &= xsrl,test為零如果符號位為0,否則test不變(處於符號位位置*/
unsigned w = sizeof(int) << 3;
int test = 1 << (w-1-k);
test &= xsrl;
int mask = ~(test - 1);
/*test為零時,~(test - 1)為全零,不會改變xsrl*/
return mask | xsrl;
}
這個題目卡了一會,主要是不能用右移比較麻煩。
編譯運行輸出:
frank@under:~/tmp$ gcc 263.c && ./a.out
0x00800000
0xff800000
2.64
#include <stdio.h>
int any_odd_one(unsigned x);
int main(int argc, char const *argv[])
{
printf("%d\t%d\n", any_odd_one(1011), any_odd_one(1024));
return 0;
}
int any_odd_one(unsigned x)
{
unsigned sizeof_unsigned = sizeof(unsigned);
unsigned w = sizeof_unsigned << 3;
return !!(x << (w-1));
}
編譯運行輸出:
frank@under:~/tmp$ gcc 264.c && ./a.out
1 0
2.65 (終於碰見個四星的。。。)
/*二分法/加法無法達到要求
第一次嘗試:
int odd_ones(unsigned x);
int main(int argc, char const *argv[])
{
int sizeof_int = sizeof(int);
return 0;
}
int odd_ones(unsigned x)
{
int mask1 = 0x55555555;
int mask2 = 0x33333333;
int mask3 = 0x0F0F0F0F;
int mask4 = 0x00FF00FF;
int mask_odd_or_even = 1
x = ((x >> 1)& mask1) + (x & mask1);
x = ((x >> 2)& mask2) + (x & mask2);
x = ((x >> 4)& mask3) + (x & mask3);
x = ((x >> 8)& mask4) + (x & mask4);
x = (x >> 16) + x;
return x & mask_odd_or_even;
}
第二次嘗試:
int odd_ones(unsigned x);
int main(int argc, char const *argv[])
{
unsigned x1 = 0xFF00FF00;
unsigned x2 = 0xFF01FF00;
printf("%d\t%d\n", odd_ones(x2), odd_ones(x1));
return 0;
}
int odd_ones(unsigned x)
{
int mask1 = 0x55555555;
int mask2 = 0x33333333;
int mask3 = 0x0F0F0F0F;
int mask4 = 0x00FF00FF;
int mask_odd_or_even = 1;
x = ((x >> 1)& mask1) + (x & mask1);
x = x & mask1;
x = (x >> 16) + x;
x = x & mask1;
x = (x >> 8) + x;
x = x & mask1;
x = (x >> 4) + x;
x = x & mask1;
x = (x >> 2) + x;
return x & mask_odd_or_even;
}
*/
//第三次嘗試:使用二分法/異或
#include <stdio.h>
int odd_ones(unsigned x);
int main(int argc, char const *argv[])
{
unsigned x1 = 0xFF00FF00;
unsigned x2 = 0xFF01FF00;
printf("%d\t%d\n", odd_ones(x2), odd_ones(x1));
return 0;
}
int odd_ones(unsigned x)
{
x = x ^ (x >> 16);
x = x ^ (x >> 8);
x = x ^ (x >> 4);
x = x ^ (x >> 2);
x = x ^ (x >> 1);
return x & 1;
}
這個題的關鍵點在於如何表示偶數(將所有“1”相加末位為0)以及類似二分法的相加方法,同時注意到每次需要用用掩碼將以前的高位置零。
這個題目是不會有“溢出”的情況的,因為1+1=10,10+10=0100,0100+0100=00001000......datalab實驗里有一個相似的題目,那個題目更難一些。
以上想法在滿足“Your code should contain a total of at most 12 arithmetic, bitwise, and logical
operations.”時出現了問題,根本原因在於二分法需要顧及到低位相加可能產生的進位,所以每次都需要用掩碼將特定的高位置零,思路有點受到之前datalab實驗的束縛(那個是要計算“1”的總數目)。這里計算的是“1”的數目的奇偶,不用考慮進位,異或運算是最佳選擇,因為1+1和0+0均產生0(代表偶數),1+0和0+1均產生1(代表奇數)。
編譯運行輸出:
frank@under:~/tmp$ gcc 265.c && ./a.out
1 0
2.66
#include <stdio.h>
#include <limits.h>
/*
* Generate mask indicating leftmost 1 in x. Assume w=32.
* For example, 0xFF00 -> Ox8000, and Ox6600 --> Ox4000.
* If x = 0, then return 0.
*/
int leftmost_one(unsigned x);
int main(int argc, char const *argv[])
{
printf("%#.8x\n", leftmost_one(0xFF00));
printf("%#.8x\n", leftmost_one(0x6600));
printf("%#.8x\n", leftmost_one(0x88886600));
printf("%#.8x\n", leftmost_one(0));
return 0;
}
int leftmost_one(unsigned x)
{
unsigned sizeof_unsigned = sizeof(unsigned);
unsigned w = sizeof_unsigned << 3;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x & ((~x >> 1)|INT_MIN);
}
/*
* Your code should contain a total of at most 15 arithmetic, bitwise, and logical
* operations.
* Hint: First transform x into a bit vector of the form [O · · · 011 · . · 1].
*/
最后與INT_MIN做或運算是為了處理0x80000000這種邊界情況,在這種情況下,~x >> 1由於沒有更高位,而x又是unsigned類型,所以最高位會是0而非1,為了適應這種情況,強制將~x >> 1最高位置1。
編譯運行輸出:
frank@under:~/tmp$ gcc 266.c && ./a.out
0x00008000
0x00004000
0x80000000
00000000
另外,Web Asides http://csapp.cs.cmu.edu/3e/waside/waside-tneg.pdf 上面有一個利用-x和x的區別在於除最右1之前位翻轉的特性求rightmost_one: x&-x, 有時間可以看看。
2.67
A:
(C11, 6.5.7p3) "If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined"
B:
#include <stdio.h>
int int_size_is_32();
int main(int argc, char const *argv[])
{
printf("%d\n", int_size_is_32());
return 0;
}
int int_size_is_32()
{
int set_msb = 1 << 31;
int beyond_msb = set_msb;
beyond_msb <<= 1;
return set_msb && !beyond_msb;
}
編譯運行輸出:
frank@under:~/tmp$ gcc 267.c && ./a.out
1
C:
#include <stdio.h>
int int_size_is_32();
int main(int argc, char const *argv[])
{
printf("%d\n", int_size_is_32());
return 0;
}
int int_size_is_32()
{
int set_msb = 1 << 15;
set_msb <<= 15;
set_msb <<= 1;
int beyond_msb = set_msb;
beyond_msb <<= 1;
return set_msb && !beyond_msb;
}
2.68
#include <stdio.h>
int lower_one_mask(int n);
int main(int argc, char const *argv[])
{
printf("%#.8x\n", lower_one_mask(6));
printf("%#.8x\n", lower_one_mask(17));
return 0;
}
int lower_one_mask(int n)
{
int sizeof_int = sizeof(int);
unsigned x = ~0;
x >>= ((sizeof_int << 3) - n);
return x;
}
編譯運行輸出:
frank@under:~/tmp$ gcc 268.c && ./a.out
0x0000003f
0x0001ffff
2.69
#include <stdio.h>
unsigned rotate_left(unsigned x, int n);
int main(int argc, char const *argv[])
{
unsigned x = 0x12345678;
printf("%#.8x\n", rotate_left(x, 0));
printf("%#.8x\n", rotate_left(x, 4));
printf("%#.8x\n", rotate_left(x, 20));
return 0;
}
unsigned rotate_left(unsigned x, int n)
{
unsigned sizeof_unsigned = sizeof(unsigned);
unsigned w = sizeof_unsigned << 3;
unsigned mask = ((1 << n)-1) << (w-n);
unsigned cache = (mask & x) >> (w-n);
x <<= n;
return x | cache;
}
關鍵點在於掩碼的產生和移除位數據的保存。
編譯運行輸出:
frank@under:~/tmp$ gcc 269.c && ./a.out
0x12345678
0x23456781
0x67812345
2.70
#include <stdio.h>
#include <limits.h>
int fits_bits(int x, int n);
int main(int argc, char const *argv[])
{
/*test short and 31bits*/
printf("%d\n", fits_bits(-32768, 16));
printf("%d\n", fits_bits(32767, 16));
printf("%d\n", fits_bits(INT_MAX, 32));
printf("%d\n", fits_bits(INT_MIN, 32));
printf("%d\n", fits_bits(0, 16));
printf("%d\n", fits_bits(0, 32));
printf("%d\n", fits_bits(32768, 16));
printf("%d\n", fits_bits(-32769, 16));
printf("%d\n", fits_bits(INT_MIN, 31));
printf("%d\n", fits_bits(INT_MAX, 31));
return 0;
}
int fits_bits(int x, int n)
{
unsigned sizeof_int = sizeof(int);
unsigned w = sizeof_int << 3;
int y = x << (w-n) >> (w-n);
return y == x;
}
編譯運行輸出:
frank@under:~/tmp$ gcc 270.c && ./a.out
1
1
1
1
1
1
0
0
0
0
2.71
/* Declaration of data type where 4 bytes are packed into an unsigned */
typedef unsigned packed_t;
/* Extract byte from word. Return as signed integer */
int xbytte(packed_t word, int bytenum);
/*That is, the function will extract the designated byte and sign extend it to be
a 32-bit int.
Your predecessor (who was fired for incompetence) wrote the following code:*/
//Failed attempt at xbyte:
int xbyte(packed_t word, int bytenum)
{
return (word>> (bytenum << 3)) & OxFF;
}
//A. What is wrong with this code?
//B. Give a correct implementation of the function that uses only left and right
//shifts, along with one subtraction.
A:
當取出的字節為負數時,由於原操作“粗暴”的將高位置零,會返回一個錯誤的正值。
B:
#include <stdio.h>
typedef unsigned packed_t;
int xbytte(packed_t word, int bytenum);
int main(int argc, char const *argv[])
{
packed_t word = 0x8008FF00;
printf("%d\n", xbytte(word, 0));
printf("%d\n", xbytte(word, 1));
printf("%d\n", xbytte(word, 2));
printf("%d\n", xbytte(word, 3));
return 0;
}
int xbytte(packed_t word, int bytenum)
{
unsigned left_move = (3 - bytenum) << 3;
unsigned right_move = (3) << 3;
return (int)word << left_move >> right_move;
}
編譯運行輸出:
frank@under:~/tmp$ gcc 271.c && ./a.out
0
-1
8
-128
2.72
/*BUGGY: Copy integer into buffer if space is available */
void copy_int(int val; void *buf, int maxbytes)
{
if (maxbytes-sizeof(val) >= 0)
memcpy(buf, (void*) &val, sizeof(val));
}
A:
sizeof返回的類型為size_t:
According to the 1999 ISO C standard (C99),
size_t
is an unsigned integer type of at least 16 bit (see sections 7.17 and 7.18.3).
size_t
is an unsigned data type defined by several C/C++ standards, e.g. the C99 ISO/IEC 9899 standard, that is defined instddef.h
.1 It can be further imported by inclusion ofstdlib.h
as this file internally sub includesstddef.h
.
所以maxbytes-sizeof(val)將一直轉化為無符號數並永遠大於等於零。
B:
void copy_int(int val; void *buf, int maxbytes)
{
if(maxbytes < 0)
return;
if (maxbytes >= sizeof(val))
memcpy(buf, (void*) &val, sizeof(val));
}
2.73
#include <stdio.h>
#include <limits.h>
int saturating_add(int x, int y);
int main(int argc, char const *argv[])
{
printf("%d\n", saturating_add(123456, -54321));
printf("%d\n", saturating_add(2147483647, 1));
printf("%d\n", saturating_add(-2147483648, -1));
return 0;
}
int saturating_add(int x, int y)
{
unsigned sizeof_int = sizeof(int);
unsigned w = sizeof_int << 8;
int i = (x ^ y) >> (w-1);//+-:FFFFFFFF ++/--:00000000
int j = ((x+y) ^ x) >> (w-1);//overflow:FFFFFFFF otherwise:00000000
int k = x >> (w-1);//+:00000000 -:FFFFFFFF
return (i & (x + y)) + (~i & (j & ( (~k & INT_MAX) + (k & INT_MIN) )));
}
解釋一下i j k:這三個變量和與運算結合用來做“判斷語句”,i通過x,y是否異號判斷是否可能溢出,j通過結果和加數的符號判斷在同號的情況下是否發生溢出。k判斷是應該返回INT_MAX 還是 INT_MIN。
編譯運行輸出:
frank@under:~/tmp$ gcc 273.c && ./a.out
69135
2147483647
-2147483648
2.74
#include <stdio.h>
#include <limits.h>
int tsub_ok(int x, int y);
int main(int argc, char const *argv[])
{
printf("%d\n", tsub_ok(123456, 54321));
printf("%d\n", tsub_ok(2147483647, -1));
printf("%d\n", tsub_ok(-2147483648, 1));
return 0;
}
int tsub_ok(int x, int y)
{
unsigned sizeof_int = sizeof(int);
unsigned w = sizeof_int << 8;
y = -y;
int i = (x ^ y) >> (w-1);//+-:FFFFFFFF ++/--:00000000
int j = ((x+y) ^ x) >> (w-1);//overflow:FFFFFFFF otherwise:00000000
return i || ~j;
}
原理與2.73類似。
編譯運行輸出:
frank@under:~/tmp$ gcc 274.c && ./a.out
1
0
0
2.75
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
unsigned unsigned_high_prod(unsigned x, unsigned y);
int signed_high_prod(int x, int y);
int main(int argc, char const *argv[])
{
/* code */
return 0;
}
unsigned unsigned_high_prod(unsigned x, unsigned y)
{
unsigned w = sizeof(int32_t) << 3;
int64_t signed_total_prod = signed_high_prod(x, y);
signed_total_prod <<= w;
signed_total_prod += x*y;
bool x_w = x < 0 ? true : false;
bool y_w = y < 0 ? true : false;
int64_t unsigned_total_prod = signed_total_prod + ((x_w*(int)y + y_w*(int)x)<<w) + x_w*y_w<<(w*2);
return (unsigned)(unsigned_total_prod>>w);
}
原理參見樹上2.18等式。
2.76
void *calloc(size_t nmemb, size_t size)
{
void *p;
if(!(nmemb*size) || !(p = malloc(size*nmemb)))
return NULL;
else if (((size_t)(nmemb*size))/size != nmemb)
/* __builtin_umull_overflow() works too */
{
/* Thanks to zhzhwz who found a forget-to-free problem here.
Maybe we should write a free_and_return_NULL block
and goto it specifically.
*/
fprintf(stderr, "size*nmemb overflow size_t.\n");
free(p);
return NULL;
}
else
{
memset(p, 0, size*nmemb);
return p;
}
}
2.77
#include <stdio.h>
int main(int argc, char const *argv[])
{
unsigned sizeof_int = sizeof(int);
unsigned w = sizeof_int << 3;
int x = 1;
printf("%d\n", (x << 4) + x);//k=17
printf("%d\n", x - (x << 3));//k=-7
printf("%d\n", (x << 6) - (x << 2));//k=60
printf("%d\n", (x << 4) - (x << 7));//k=-112
return 0;
}
編譯運行輸出:
frank@under:~/tmp$ gcc 277.c && ./a.out
17
-7
60
-112
2.78
#include <stdio.h>
int divide_power2(int x, int k);
int main(int argc, char const *argv[])
{
printf("%d\n", divide_power2(1024, 2));
printf("%d\n", divide_power2(5, 2));
printf("%d\n", divide_power2(-1024, 2));
printf("%d\n", divide_power2(-5, 2));
return 0;
}
int divide_power2(int x, int k)
{
int bias = (1 << k) - 1;
unsigned sizeof_int = sizeof(int);
unsigned w = sizeof_int << 3;
int judge = x >> (w-1); //-:FFFFFFFF +:00000000
return (judge & ((x + bias) >> k)) + (~judge & (x >> k));
}
編譯運行輸出:
frank@under:~/tmp$ gcc 278.c && ./a.out
256
1
-256
-1
2.79
int mul3div4(int x)
{
int k = 2;
int bias = (1 << k) - 1;
unsigned sizeof_int = sizeof(int);
unsigned w = sizeof_int << 3;
x = (x << 1) + x;
int judge = x >> (w-1); //-:FFFFFFFF +:00000000
return (judge & ((x + bias) >> k)) + (~judge & (x >> k));
}
2.80
[BUG] threefourths(3)應計算得到2,代碼將會得到0,可以參考 2.80
From: zhzhwz
int threefourths(int x)
{
int k = 2;
int bias = (1 << k) - 1;
unsigned sizeof_int = sizeof(int);
unsigned w = sizeof_int << 3;
int judge = x >> (w-1); //-:FFFFFFFF +:00000000
x = (judge & ((x + bias) >> k)) + (~judge & (x >> k));
return (x << 1) + x;
}
參考2.78
2.81
A:
x = ~0 << k;
B:
x = ~(~0 << k) << j;
2.82
A: when x = INT_MIN, y = 0. Yields 0
B: It always yields 1. Say, "mod" = mod 2^32. LEFT = ((((((x+y)mod)*16)mod)+y)mod)-x)mod = (17y + 15x)mod. RIGHT = (17*y)mod + (15*x)mod = (17y + 15x)mod.
C: It always yields 1. LEFT = (~x + 1) + (~y + 1) - 1 = -x + -y - 1. RIGHT = ~(x+y) + 1 - 1 = -(x+y) - 1 = -x + -y - 1.
D: It always yields 1. Since whether a integer type data is a int or unsigned doesn't influence the implementations of subtraction or unary minus operators.
(寫到一半突然發現是用英文寫的,可能是看教材影響的。。。)
E: 永遠產生1,因為x先向右移動,再向左移動相同的位置,所以數據的高位不會受到影響。但是如果低兩位有1的話,會都變為0。因為低位的1在補碼中無論對於負數還是正數都是加的。所以對於正數來說,值會變小或者不變;對於0來說,值會不變;對於負數來說,值會變小或者不變。
2.83
A:
根據提示:Y = x*2^k - x 即 x = Y/(2^k - 1)
B:
由A:(a) Y = 101 = 5, k = 3, x = 5/7 (b) Y = 0110 = 6, k = 4, x = 2/5 (c) Y = 010011 = 19, k = 6, x = 19/63
2.84
#include <stdio.h>
int float_le(float x, float y);
unsigned f2u(float x);
int main(int argc, char const *argv[])
{
printf("%d\n", float_le((float)1.11, (float)1.10));
printf("%d\n", float_le((float)-1.2, (float)3.0));
printf("%d\n", float_le((float)1.3, (float)1.3));
printf("%d\n", float_le((float)0, (float)0));
printf("%d\n", float_le((float)-1.1, (float)0));
printf("%d\n", float_le((float)0, (float)1.1));
return 0;
}
unsigned f2u(float x)
{
return *(unsigned*)&x;
}
int float_le(float x, float y)
{
unsigned ux = f2u(x);
unsigned uy = f2u(y);
/*Get the sign bits*/
unsigned sx = ux >> 31; //+:0 -:1
unsigned sy = uy >> 31;
/* Give an expression using only ux, uy, sx, and sy */
return (sx ^ sy) ? (sx ? 1 : 0)/*-+ +-*/ : (sx ? (ux>=uy) : (ux<=uy))/*-- ++*/;
}
編譯運行輸出:
frank@under:~/tmp$ gcc 284.c && ./a.out
0
1
1
1
1
1
[PATCH] +0和-0不等
From: zhzhwz
@@ -11,6 +11,7 @@ int main(int argc, char const *argv[])
printf("%d\n", float_le((float)0, (float)0));
printf("%d\n", float_le((float)-1.1, (float)0));
printf("%d\n", float_le((float)0, (float)1.1));
printf("%d\n", float_le((float)(1/1e100), (float)(-1/1e100)));
return 0;
}
@@ -28,5 +29,5 @@ int float_le(float x, float y)
unsigned sy = uy >> 31;
/* Give an expression using only ux, uy, sx, and sy */
return (sx ^ sy) ? (sx ? 1 : 0)/-+ +-/ : (sx ? (ux>=uy) : (ux<=uy))/-- ++/;
return (ux << 1 == 0 && uy << 1 == 0)/all 0/ || ((sx ^ sy) ? (sx ? 1 : 0)/-+ +-/ : (sx ? (ux>=uy) : (ux<=uy))/-- ++/);
}
\ No newline at end of file
2.85
bias = 2^(k-1) - 1 suppose that k <= n
A:
E = 0b10+bias, M = 0b1.11, f = ob1100*, V = 1.0
bit representation: 0, 0b10+bias, 1100*
B:
E = n+bias, M = 0b1.11*, f = 0b11*, V = 2^(n+1)-1
bit representation: 0, n+bias, 11*
C:
The smallest positive normalized value : E = 0b00*1, M = 0b1.00*, f = 0b00*, V = 1.0
So the reciprocal is exactly the same number.
2.86
bias = 2^14 - 1
Smallest positive denormalized:
Value: 0, 00*, 0, 00*1 Decimal:2(2-214) * 2^(-63)
Smallest positive normalized:
Value: 0, 00*1, 1, 00* Decimal:2(2-214)
Largest normalized:
Value: 0, 11*0, 1, 11* Decimal: 2(214-1) * (2-2^(-63))
2.87和2.88本來在Typora上是用表格寫的,上傳上來好像有格式問題,將就看一下 ; )
2.87
| Description | Hex | M | E | V | D |
| -0 | 8000 | 0 | -14 | 0 | 0 |
| Smallest value > 2 | 4001 | 1025/1024 | 1 | 10252^-8 | 2.001953 |
| 512 | 6000 | 1 | 9 | 12^9 | 512.000000 |
| Largest denormalized | 0311 | 1023/1024 | -14 | 10232^-24 | 0.000061 |
| negative infinite | FC00 | - | - | - | -inf |
| 3BB0 | 3BB0 | 124/64 | -1 | 312^-5 | 0.968750 |
2.88
| Format A | Format A | Format B | Format B |
| Bits | Value | Bits | Value |
| 1 01111 001 | -9/8 | 1 0111 0010 | -9/8 |
| 0 10110 011 | 112^4 | 0 1110 0110 | 112^4 |
| 1 00111 010 | -52^-10 | 1 0000 0101 | -52^-10 |
| 0 00000 111 | 72^-17 | 0 0000 0001 | 2^-10 |
| 1 11100 000 | -2^13 | 1 1110 1111 | -312^3 |
| 0 10111 100 | 32^7 | 0 1110 1111 | 312^3 |
2.89
A:總是返回1.因為int到double不會有精度上的損失,所以x,dx轉float(損失精度)的結果是一樣的。
B:不總是返回1.如x=INT_MIN,Y=1。
[PATCH] 注意2.89的C中dz是由一個整數轉換來的,因此不會取到1e-30
From: zhzhwz
- C:不總是返回1.浮點數不滿足結合律,如dx=1e30, dy=-1e30, dz=1e-30。
+ C:由於int轉換為double不會有精度上的損失,且在int范圍內使用double做加法得到的結果一定是精確的(這是因為double的尾數足以容納32bit),因此滿足結合律。
D:不總是返回1.原因同上。例如dx與dy互為倒數且dy*dz=+infinite。
E:不總是返回1.例如dx=1.0, dz=0.0 。
2.90
float fpwr2(int x)
{
/* Result exponent and fraction */
unsigned exp, frac;
unsigned u;
if (x < -149)
{
/* Too small. Return 0.0 */
exp = 0;
frac = 0;
}
else if (x < -126)
{
/* Denormalized result */
exp = 0;
frac = 1 << (149 + x);
}
else if (x < 128)
{
/* Normalized result. */
exp = x + 127;
frac = 0;
}
else
{
/* Too big. Return +oo */
exp = 0xFF;
frac = 0;
}
/*Pack exp and frac into,32 bits */
u = exp << 23 | frac;
/* Return as float */
return u2f(u);
}
2.91
0x 40490FDB = 0b 0100 0000 0100 1001 0000 1111 1101 1011 = 0,10000000,10010010000111111011011
A: 10010010000111111011011
B: (詳情可見)2.83 y=1, k=3, 即 0b11.(001)*
C: 0x4049039b 0x40492492 從高位向低位第19個。
浮點數部分由於時間所限,沒有進行相關測試,思路大致應該是對的,可能會有一些邊界/特殊情況會產生問題,歡迎指出。
2.92
float_bits float_negate(float_bits f)
{
unsigned sign = f >> 31;
unsigned exp = f >> 23 & 0xFF;
unsigned frac = f & 0x7FFFFF;
if(!(exp ^ 0xFF) && frac)
{
return f;
}
else
{
sign = !sign;
return (sign << 31) | (exp << 23) | frac;
}
}
2.93
float_bits float_absval(float_bits f)
{
unsigned exp = f >> 23 & 0xFF;
unsigned frac = f & 0x7FFFFF;
if(!(exp ^ 0xFF) && frac)
{
return f;
}
return (exp << 23) | frac;
}
2.94
float_bits float_twice(float_bits f)
{
unsigned sign = f >> 31;
unsigned exp = f >> 23 & 0xFF;
unsigned frac = f & 0x7FFFFF;
if(!(exp ^ 0xFF))
{
if (frac)//NaN
{
return f;
}
/*else
{
return (sign << 31) | (exp << 23) | frac;//infinite
}*/
}
else//Denormalnized and normalized
{
if (exp)//Normalnized
{
if (!(frac >> 22))
{
frac <<= 1;
//return (sign << 31) | (exp << 23) | frac;
}
else if (!(exp ^ 0xFE))
{
++exp;
//return (sign << 31) | (exp << 23) | frac;
}
else//overflow
{
frac = 0;
exp = 0xFF;
//return (sign << 31) | (exp << 23) | frac;
}
}
else//Denormalized
{
if (!(frac >> 22))
{
frac <<= 1;
//return (sign << 31) | (exp << 23) | frac;
}
else//Turn to Normalized
{
++exp;
frac = frac << 10 >> 9;//set the 23th bit of frac to 0 and then left shift one bit.(注釋是必要的。。。過了幾天看這一段的時候自己也沒弄懂,忘了這里frac是一個unsigned。。。)
//return (sign << 31) | (exp << 23) | frac;
}
}
}
return (sign << 31) | (exp << 23) | frac;
}
2.95
float_bits float_half(float_bits f)
{
unsigned sign = f >> 31;
unsigned exp = f >> 23 & 0xFF;
unsigned frac = f & 0x7FFFFF;
if(!(exp ^ 0xFF))
{
if (frac)//NaN
{
return f;
}
/*else
{
return (sign << 31) | (exp << 23) | frac;//infinite
}*/
}
else//Denormalnized and normalized
{
if (exp)//Normalnized
{
if (exp != 1)
{
--exp;
//return (sign << 31) | (exp << 23) | frac;
}
else//Turn to Denormalnized
{
if (frac)//maybe need to round to even
{
if ((frac >> 1)&1)
{
++frac;
frac >>= 1;
frac |= 0x400000;
--exp;
}
else
{
frac >>=1;
frac |= 0x400000;
--exp;
}
}
}
}
else//Denormalized
{
if (frac)//maybe need to round to even
{
if ((frac >> 1)&1)
{
++frac;
frac >>= 1;
}
else
{
frac >>= 1;
}
}
}
}
return (sign << 31) | (exp << 23) | frac;
}
2.96
int float_f2i(float_bits f)
{
unsigned sign = f >> 31;
unsigned exp = f >> 23 & 0xFF;
unsigned frac = f & 0x7FFFFF;
unsigned bias = 127;
int flag = 0;
if(sign)//<=0
{
if (f < 0xBF800000)//>-1
{
return 0;
}
else if (f <= 0xCF000000)
{
if (f == 0xCF000000)//INT_MIN
{
return INT_MIN;//0x80000000;
}
else
{
f &= 0x7FFFFFFF;//first treat it as a positive number
flag = 1;
goto A;
B:
return (~f + 1);//-(+int)
}
}
else//overflow/-infinite/NaN
{
return 0x80000000;
}
}
else//>=0
{
if (f < 0x3F800000)//<1//Denormalnized->0
{
return 0;
}
else if (f <= 0x4EFFFFFF)
{
A:
frac |= 0x800000;
unsigned move = 23 - (exp-bias);
if (flag)//jumped from a negative number
{
f = move >= 0 ? frac >> move : frac << -move;
goto B;
}
else
{
return move >= 0 ? frac >> move : frac << -move;
}
}
else//overflow/+infinite/NaN
{
return 0x80000000;
}
}
}
標准答案
/* Compute (int) f. If conversion causes overflow or f is NaN, return 0x80000000 */
int float_f2i(float_bits f) {
unsigned sign = f >> 31;
unsigned exp = (f >> 23) & 0xFF;
unsigned frac = f & 0x7FFFFF;
/* Create normalized value with leading one inserted, and rest of significand in bits 8--30./
unsigned val = 0x80000000u + (frac << 8);
if (exp < 127) {
/* Absolute value is < 1 */
return (int) 0;
}
if (exp > 158)
/* Overflow */
return (int) 0x80000000u;
/* Shift val right */
val = val >> (158 - exp);
/* Check if out of range */
if (sign) {
/* Negative */
return val > 0x80000000u ? (int) 0x80000000u : -(int) val;
} else {
/* Positive */
return val > 0x7FFFFFFF ? (int) 0x80000000u : (int) val;
}
}
2.97
int leftmost_one(unsigned x)
{
unsigned sizeof_unsigned = sizeof(unsigned);
unsigned w = sizeof_unsigned << 3;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x & ((~x >> 1)|INT_MIN);
}
float_bits float_i2f(int i)
{
unsigned sign = 0;
unsigned exp = 0;
unsigned frac = 0;
unsigned bias = 127;
if (i == INT_MIN)
{
return 0xCF000000;
}
else//treat negative as positive
{
if (i < 0)
{
sign = 1;
i = -i;
}
int mask = leftmost_one(i);
int move = 0;
if (mask >= 0x00800000)//rightshift
{
while(mask != 0x00800000)
{
mask >>= 1;
++move;
}
if ((i & ((1 << (move+1)) - 1)) > (1 << move))//round to even(>1/2)
{
i >>= move;
i += 1;
}
else if((i & ((1 << (move+1)) - 1)) < (1 << move))//(<1/2)
{
i >>= move;
}
else// 1/2
{
if ((i >> move)&1)//round to even
{
i >>= move;
i += 1;
}
else
{
i >>= move;
}
}
}
else//leftshift
{
while(mask != 0x00800000)
{
mask <<= 1;
--move;
}
i <<= -move;
}
frac = i & 0x7FFFFF;//Discard the 24th bit one
exp = bias + 22 + move;
}
return (sign << 31) | (exp << 23) | frac;
}
用到了2.66產生標志整數最高位1掩碼,從而判斷應該左移或者右移多少位。