在最早學習四則運算的過程中,我們其實就已經掌握了進位演算法,這一次我將對二進位運用這個進位演算法來實現四則運算。 四則運算 math.c 從遞歸角度看待代碼 遞歸是函數調用,考慮值的傳遞過程,適合閱讀。 迭代是具體的實現過程,往往代碼效率更加充分。 通常我們在寫代碼時,往往註重代碼的效率和正確性,而忽略 ...
在最早學習四則運算的過程中,我們其實就已經掌握了進位演算法,這一次我將對二進位運用這個進位演算法來實現四則運算。
四則運算
math.c
/**
* 功能:通過位操作實現四則運算
* 演算法:完全參照十進位的進位演算法
*
* Created with CLion
* User: zzzz76
* Date: 2018-02-10
*/
#include <stdio.h>
#include <assert.h>
#include "math.h"
/**
* 加法:往上遞歸實現
*
* @param a
* @param b
* @return
*/
int base_add(int a, int b) {
/* 值在函數中的傳遞只能通過參數或者返回操作,所以遞歸效果無非是體現在參數和返回操作上 */
if (b == 0) {
return a;
}
int save = a ^b;
int promote = (a & b) << 1;
return base_add(save, promote);
}
/**
* 加法:迭代實現
*
* @param a
* @param b
* @return
*/
int base_add_re(int a, int b) {
while (a && b) {
int promote = (a & b) << 1;
a = a ^ b;
b = promote;
}
return a ^ b;
}
/**
* 減法:往上遞歸實現
*
* @param a
* @param b
* @return
*/
int base_sub(int a, int b) {
if (b == 0) {
return a;
}
int save = a ^ b;
int reduce = ((~a) & b) << 1;
return base_sub(save, reduce);
}
/**
* 減法:迭代實現
*
* @param a
* @param b
* @return
*/
int base_sub_re(int a, int b) {
while(b) {
int save = a ^ b;
b = ((~a) & b) << 1;
a = save;
}
return a;
}
/**
* 減法:補位實現
*
* @param a
* @param b
* @return
*/
int base_sub_re_re(int a, int b) {
return base_add(a, base_add(~b, 1));
}
/**
* 乘法:遞歸實現
*
* @param a
* @param b
* @return
*/
int base_mul(int a, int b) {
int count = 0;
if (a == 0) {
return count;
}
if (a & 1) {
count = base_add(count, b);
}
a = (unsigned)a >> 1;
b <<= 1;
count = base_add(count, base_mul(a, b));
return count;
}
/**
* 乘法:迭代實現
*
* @param a
* @param b
* @return
*/
int base_mul_re(int a, int b) {
int count = 0;
while (a) {
if (a & 1) {
count = base_add(count, b);
}
a = (unsigned)a >> 1;
b <<= 1;
}
return count;
}
/**
* 除法:迭代實現
*
* @param a
* @param b
* @return
*/
int base_div(int a, int b) {
assert(b);
int result = 0;
int bit_num = 31;
while (bit_num != -1) {
if (b <= ((unsigned) a >> bit_num)) {
result = base_add(result, 1 << bit_num);
a = base_sub(a, b << bit_num);
}
bit_num = base_sub(bit_num, 1);
}
return result;
}
/**
* 除法:遞歸實現
*
* @param a
* @param b
* @return
*/
int base_div_re(int a, int b) {
assert(b);
if (a < (unsigned) b) {
return 0;
}
int bit_num = 0;
while (b <= (unsigned) a >> 1 >> bit_num) {
bit_num = base_add(bit_num, 1);
}
int result = 1 << bit_num;
a = base_sub(a, b << bit_num);
result = base_add(result, base_div_re(a, b));
return result;
}
math.h
/**
* Created with CLion
* User: zzzz76
* Date: 2018-02-12
*/
#ifndef MATH_H
#define MATH_H
int base_add(int a, int b);
int base_add_re(int a, int b);
int base_sub(int a, int b);
int base_sub_re(int a, int b);
int base_sub_re_re(int a, int b);
int base_mul(int a, int b);
int base_mul_re(int a, int b);
int base_div(int a, int b);
int base_div_re(int a, int b);
#endif //MATH_H
test.c
/**
* Created with CLion
* User: zzzz76
* Date: 2018-02-12
*/
#include "math.h"
#include <stdio.h>
static int main_ret = 0;
static int test_count = 0;
static int test_pass = 0;
#define EXPECT_EQ_BASE(expect, actual, format) \
do {\
test_count++;\
if ((expect) == (actual)) {\
test_pass++;\
} else {\
fprintf(stderr, "%s:%d: expect: " format " actual: " format "\n", __FILE__, __LINE__, expect, actual);\
main_ret = 1;\
}\
} while(0);
#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE(expect, actual, "%d");
#define TEST_BASE_ADD(expect, a, b) \
EXPECT_EQ_INT(expect, base_add(a, b));\
EXPECT_EQ_INT(expect, base_add_re(a, b));
static void test_base_add() {
TEST_BASE_ADD(2, 1, 1);
TEST_BASE_ADD(0, -1, 1);
TEST_BASE_ADD(0, 1, -1);
TEST_BASE_ADD(-2, -1, -1);
TEST_BASE_ADD(-2147483648, 2147483647, 1);
}
#define TEST_BASE_SUB(expect, a, b) \
EXPECT_EQ_INT(expect, base_sub(a, b));\
EXPECT_EQ_INT(expect, base_sub_re(a, b));\
EXPECT_EQ_INT(expect, base_sub_re_re(a, b));
static void test_base_sub() {
TEST_BASE_SUB(0, 1, 1);
TEST_BASE_SUB(-2, -1, 1);
TEST_BASE_SUB(2, 1, -1);
TEST_BASE_SUB(0, -1, -1);
TEST_BASE_SUB(2147483647, -2147483648, 1);
}
#define TEST_BASE_MUL(expect, a, b) \
EXPECT_EQ_INT(expect, base_mul(a, b));\
EXPECT_EQ_INT(expect, base_mul_re(a, b));
static void test_base_mul() {
TEST_BASE_MUL(9, 3, 3);
TEST_BASE_MUL(-9, -3, 3);
TEST_BASE_MUL(-9, 3, -3);
TEST_BASE_MUL(9, -3, -3);
TEST_BASE_MUL(0, -2147483648, 2);
TEST_BASE_MUL(-2, 2147483647, 2);
}
#define TEST_BASE_DIV(expect, a, b) \
EXPECT_EQ_INT(expect, base_div(a, b));\
EXPECT_EQ_INT(expect, base_div_re(a, b));
static void test_base_div() {
TEST_BASE_DIV(2, 2, 1);
TEST_BASE_DIV(-2, -2, 1);
TEST_BASE_DIV(0, 2, -1);
TEST_BASE_DIV(0, -2, -1);
TEST_BASE_DIV(0, 1, 2);
TEST_BASE_DIV(0, 1, -2);
TEST_BASE_DIV(2147483647, -1, 2);
TEST_BASE_DIV(1, -1, -2);
}
static void test_base() {
test_base_add();
test_base_sub();
test_base_mul();
test_base_div();
}
int main() {
test_base();
printf("%d/%d (%3.2f%%) passed\n", test_pass, test_count, test_pass * 100.0 / test_count);
return main_ret;
}
從遞歸角度看待代碼
遞歸是函數調用,考慮值的傳遞過程,適合閱讀。
迭代是具體的實現過程,往往代碼效率更加充分。
通常我們在寫代碼時,往往註重代碼的效率和正確性,而忽略了代碼表達的意思,致使代碼難以閱讀。所以對兩者的取捨,一定程度影響了代碼的好與壞