V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ellermister
V2EX  ›  程序员

能否用你所熟悉的开发语言,实现基础四则运算?

  •  
  •   ellermister · 2023-06-11 17:40:10 +08:00 · 2676 次点击
    这是一个创建于 516 天前的主题,其中的信息可能已经有所发展或是发生改变。

    要求:

    • 不能使用内联汇编 /内嵌其他语言
    • 不能使用网络 API
    • 不能使用编程语言提供的基础四则运算语法或函数

    例题

    • 1+2 = 3
    • 4*8 = 32
    • 7/2 = 3.5 / 3
    • 2-2 = 0

    突然想到,这种有没有好的思路实现啊 ?

    18 条回复    2023-06-12 10:51:34 +08:00
    fengjianxinghun
        1
    fengjianxinghun  
       2023-06-11 18:04:01 +08:00   ❤️ 1
    eval("1+2")
    ellermister
        2
    ellermister  
    OP
       2023-06-11 18:05:35 +08:00   ❤️ 1
    @fengjianxinghun 不算,这个太变态了
    Mohanson
        3
    Mohanson  
       2023-06-11 18:17:58 +08:00
    先实现半加器, 再使用半加器实现全加器, 这样你就实现了加法.

    有了加法就有了减法.

    有了加法就有了乘法.

    有了加, 减, 乘就能实现除法.
    2113ic
        4
    2113ic  
       2023-06-11 19:02:50 +08:00
    @ellermister new function('exp', 'return exp')😂
    vituralfuture
        5
    vituralfuture  
       2023-06-11 19:42:20 +08:00   ❤️ 2
    转补码,用逻辑运算实现一个全加器,然后级联做成 32 位全加器,这样就有了做加减法的能力,乘除法同理,计算出的结果可以转回原码再求出真值

    不过如何实现真值到原码到补码?很简单,利用按位逻辑运算,比如 C++中,一条语句`x=1;`,那么 x 就对应内存中一个存储位置,可以用按位逻辑运算取出每一位,然而非常巧的是,内存中的数值是以补码形式存储的,所以就能够取出补码每一位,也就是说不需要求补码,补码已经躺在内存里了

    我写了一个程序来简单说明,只有循环用到了加法`i++`,但是所有循环的次数都是确定的,也就是说**循环可以展开,替换成循环体重复 32 次,相当于没有使用加法**
    ```
    #include <iostream>

    using namespace std;


    void getComplementCode(bool arr[32], int num) {
    int mask = 1;
    for (int i = 0; i < 32; i++) {
    arr[i] = num & mask;
    num = num >> 1;
    }
    }

    int getTrueValue(const bool arr[32]){
    int s = 0;
    for (int i = 0; i < 32; i++) {
    s = s | (arr[i] << i);
    }
    return s;
    }



    void add1(bool a, bool b, bool &s, bool c0, bool &c1) {
    s = a ^ b ^ c0;
    c1 = (a && b) || ((a ^ b) && c0);
    }

    void add32(bool a[32], bool b[32], bool s[32], bool c = false) {
    bool carray[32];
    add1(a[0], b[0], s[0], c, carray[0]);
    for (int i = 1; i < 32; i++) {
    add1(a[i], b[i], s[i], carray[i-1], carray[i]);
    }
    }

    void getTwoComplement(bool arr[32]){
    for(int i=0;i<32;i++){
    arr[i] = ! arr[i];
    }
    bool temp[32];
    bool one[32];
    one[0] = true;
    for(int i=1;i<32;i++){
    one[i] = false;
    }
    add32(arr, one, temp);
    for(int i=0;i<32;i++){
    arr[i] = temp[i];
    }
    }

    int add(int a, int b){
    bool A[32], B[32], S[32];
    getComplementCode(A, a);
    getComplementCode(B, b);
    add32(A, B, S);
    return getTrueValue(S);
    }

    int sub(int a, int b){
    bool A[32], B[32], S[32];
    getComplementCode(A, a);
    getComplementCode(B, b);
    getTwoComplement(B);
    add32(A, B, S);
    return getTrueValue(S);
    }


    int main() {
    cout << add(10, 5) << endl;
    return 0;
    }
    ```

    乘法的话,32 位加 32 位结果是 64 位的,按照这个方式也能够实现

    我写的了一个测试用例如下
    ```
    BOOST_AUTO_TEST_CASE(addTest1){
    const int times = 10000;
    int a = (int)random();
    int b = (int)random();
    BOOST_TEST(a + b == add(a, b));
    }
    ```
    测试通过


    除法和浮点数运算,计组课上没讲,不知道具体怎么做,不过道理都是相同的,模拟机器运算,把 C++写成 verilog
    vituralfuture
        6
    vituralfuture  
       2023-06-11 19:45:34 +08:00
    @vituralfuture 测试写错了,修改如下
    ```
    BOOST_AUTO_TEST_CASE(addTest1) {
    const int times = 10000;
    int a, b;
    for (int i = 0; i < times; i++) {
    a = (int) random();
    b = (int) random();
    BOOST_TEST(a + b == add(a, b));
    }
    }
    ```
    结果
    yankebupt
        7
    yankebupt  
       2023-06-11 19:51:58 +08:00
    你就直接问 GPT ,如何使用 AND OR XOR NOT 四个操作符实现加减乘除不就完了……
    是人干的事么……
    话说经常有直播网站加载 wasm ,为了实现高于目标机器位数的乘除法(估计用于签名或加解密),可以略微参考一下,也不能算是完全没有用处。
    javak
        8
    javak  
       2023-06-11 20:25:28 +08:00
    这个太简单了,十多年前,我大二的时候,《数据结构与算法设计》这门课,其中一节课的作业而已。
    test0x01
        9
    test0x01  
       2023-06-11 20:41:53 +08:00 via Android
    利用 AST,很多语言提供现成的解析器,自己再后继处理下。这样算不算?
    agagega
        10
    agagega  
       2023-06-11 20:43:46 +08:00 via iPhone
    你的语言表述很不清晰。究竟是「不用这门语言的四则运算符实现四则运算」还是「用这门语言实现一个四则运算表达式的计算工具」?
    maggch97
        11
    maggch97  
       2023-06-11 21:21:07 +08:00
    @agagega 按照这个表述我觉得问的应该就是"如何用栈实现基础四则运算",否则不会有这么拧巴的表述
    duke807
        12
    duke807  
       2023-06-11 21:30:40 +08:00 via Android
    没意义,即便你写一个 cpu 仿真代码,仿真 cpu 寄存器,依然会用到 “编程语言提供的基础四则运算”

    建议 op 直接学 verilog ,可以帮助理解最最底层的东西
    jorneyr
        13
    jorneyr  
       2023-06-11 22:31:09 +08:00
    数据结构与算法的基础:逆波兰表达式。
    lopssh
        14
    lopssh  
       2023-06-11 22:36:42 +08:00 via Android
    很基础啊...逆波兰式+表达式栈求值。
    phcbest
        15
    phcbest  
       2023-06-11 22:48:32 +08:00
    ```kotlin
    fun add(a: Int, b: Int): Int {
    var carry: Int
    var sum: Int
    do {
    sum = a xor b
    carry = (a and b) shl 1
    a = sum
    b = carry
    } while (b != 0)
    return a
    }

    fun subtract(a: Int, b: Int): Int {
    return add(a, add(b.inv(), 1))
    }

    fun multiply(a: Int, b: Int): Int {
    var result = 0
    var shift = 0
    var bCopy = b
    while (bCopy != 0) {
    if (bCopy and 1 == 1) {
    result = add(result, a shl shift)
    }
    shift++
    bCopy = bCopy shr 1
    }
    return result
    }

    fun divide(a: Int, b: Int): Int {
    var dividend = a
    var divisor = b
    var quotient = 0
    while (dividend >= divisor) {
    dividend = subtract(dividend, divisor)
    quotient = add(quotient, 1)
    }
    return quotient
    }
    ```
    zktsin
        16
    zktsin  
       2023-06-11 23:45:23 +08:00 via iPhone   ❤️ 1
    阿隆佐·邱奇提出的 lambda 演算是一种形式系统,在它中,所有的函数都只有一个输入。你可以使用它来表示所有可计算的函数。为了用它实现加减乘除四则运算,我们先需要定义一些基础函数。

    以下是用 JavaScript ES6 表示的四则运算的 lambda 演算:

    ```javascript
    const ZERO = f => x => x;
    const ONE = f => x => f(x);
    const TWO = f => x => f(f(x));
    const THREE = f => x => f(f(f(x)));

    const SUCC = n => f => x => f(n(f)(x));
    const PLUS = m => n => f => x => m(f)(n(f)(x));
    const MULT = m => n => f => m(n(f));
    const PRED = n => f => x => n(g => h => h(g(f)))(u => x)(u => u);
    const SUB = m => n => n(PRED)(m);

    const toInteger = n => n(x => x + 1)(0);

    let four = SUCC(THREE);
    console.log(toInteger(four)); // 4

    let three = PRED(four);
    console.log(toInteger(three)); // 3

    let seven = PLUS(three)(four);
    console.log(toInteger(seven)); // 7

    let twelve = MULT(two)(six);
    console.log(toInteger(twelve)); // 12

    let two = SUB(four)(two);
    console.log(toInteger(two)); // 2
    ```

    这段代码首先定义了一些常数,如 `ZERO`、`ONE`、`TWO`、`THREE`。然后,定义了四则运算的函数 `SUCC`、`PLUS`、`MULT`、`PRED` 和 `SUB`。

    其中 `SUCC` 是后继函数,表示加一操作。`PLUS` 是加法函数,它将两个数相加。`MULT` 是乘法函数,它将两个数相乘。`PRED` 是前驱函数,表示减一操作。`SUB` 是减法函数,它将两个数相减。

    注意,所有的这些函数都是通过函数组合和应用来定义的,没有使用任何 JavaScript 特有的语法,完全按照邱奇的理论来实现。

    然后,`toInteger` 函数用于将这些邱奇数转换为 JavaScript 中的整数,便于我们查看和验证结果。

    最后的部分是一些运算示例,验证了这些函数的正确性。

    注意,以上代码中的所有数都是邱奇数,它们都是以单参数函数的形式表示的。例如,`THREE` 是一个函数,它接受一个函数 `f` 和一个参数 `x`,并返回 `f(f(f(x)))`。这是邱奇数的标准表示方式。
    LykorisR
        18
    LykorisR  
       2023-06-12 10:51:34 +08:00
    大一上计算机系统基础实验课程,和这个一模一样
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2605 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 15:05 · PVG 23:05 · LAX 07:05 · JFK 10:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.