Dodawanie dwóch liczb 32-bitowych za pomocą rejestrów tylko 16-bitowych

0

Próbuję dodać dwie liczby 32-bitowe, ale używając do tego intów tylko 16-bitowych:

#include <cstdint>
#include <cstdio>
#include <bitset>
#include<iostream>

uint16_t add_hi(uint16_t a, uint16_t b)
{   
    uint16_t a_lo = (uint8_t)a;
    uint16_t a_hi = a >> 8;
    uint16_t b_lo = (uint8_t)b;
    uint16_t b_hi = b >> 8;

    uint16_t a_b_carry_low = (a_lo + b_lo) >> 8;
    uint16_t a_b_carry_high = (a_hi + b_hi) >> 8;

    uint16_t addhi = a_b_carry_low + a_b_carry_high;
    if (addhi > 1) { addhi = 1; }

    return addhi;
}

int main()
{   
    uint32_t r = 2135768218;
    std::cout << r << "\n";
    

    uint16_t a_low = (uint16_t)r;
    uint16_t a_hi = r >> 16;
    uint16_t b_low = (uint16_t)r;
    uint16_t b_hi = r >> 16;

    uint16_t wynik_hi = add_hi(a_low, b_low) + a_hi + b_hi;
    uint16_t wynik_low = a_low + b_low;

    uint32_t wynik = (wynik_hi << 16) | wynik_low;

    uint32_t wynik_1 = r + r;

    std::cout << wynik << " " << wynik_1 << "\n";
}

Wynik jest zgodny z prawdziwym wynikiem wynik_1 dla wielu liczb, ale nie dla 2135768218 i kilku innych. Funkcja add_hi służy do wyznaczenia górnego bitu z dodawania dolnych części naszych dwóch 32-bitowych liczb. Następnie obliczam górną część wyniku wynik_hi i dolną wynik_low, która jest trywialna do wyznaczenia.

Co robię źle?

2

Nie rozumiem idei, którą tu implementujesz. Przy dodawaniu carry może mieć wartość 1 lub 0 i żadną inną.

Całość wydaje się znacząco przekomplikowana, jeśli chcesz policzyć carry to zrób to tak:

uint16_t carry(uint16_t a, uint16_t b)
{   
    uint16_t sum = a+b;
    return sum >= std::max(a, b) ? 0 : 1;
}

https://wandbox.org/permlink/BWW1O0Da6DIV0pvv

0
kq napisał(a):

Nie rozumiem idei, którą tu implementujesz. Przy dodawaniu carry może mieć wartość 1 lub 0 i żadną inną.

Dlatego zrobiłem tam warunek if, który zamieniał każdy większy wynik niż 1 z powrotem na 1.

Całość wydaje się znacząco przekomplikowana, jeśli chcesz policzyć carry to zrób to tak:

uint16_t carry(uint16_t a, uint16_t b)
{   
    uint16_t sum = a+b;
    return sum > std::max(a, b) ? 0 : 1;
}

https://wandbox.org/permlink/zfxmA8uHc25ZZt5Z

Ok, to działa. Dzięki.

2
kq napisał(a):

Nie rozumiem idei, którą tu implementujesz. Przy dodawaniu carry może mieć wartość 1 lub 0 i żadną inną.

Całość wydaje się znacząco przekomplikowana, jeśli chcesz policzyć carry to zrób to tak:

uint16_t carry(uint16_t a, uint16_t b)
{   
    uint16_t sum = a+b;
    return sum > std::max(a, b) ? 0 : 1;
}

https://wandbox.org/permlink/zfxmA8uHc25ZZt5Z

Wystarczy (powinno być) tak:

uint16_t carry(uint16_t a, uint16_t b)
{
    uint16_t sum = a+b;
    return sum < a;
}

https://godbolt.org/z/GKvdKe9oe

0

Wystarczy (powinno być) tak:

uint16_t carry(uint16_t a, uint16_t b)
{
    uint16_t sum = a+b;
    return sum < a;
}

https://godbolt.org/z/GKvdKe9oe

Na pewno? Dodawanie jest przemienne, więc funkcja powinna zwracać ten sam wynik również po zamianie a i b miejscami.

Może chodziło o to?

uint16_t carry(uint16_t a, uint16_t b)
{   
    uint16_t sum = a+b;
    return sum < std::max(a, b);
}

Albo to, co jest dokładnie tym samym

uint16_t carry(uint16_t a, uint16_t b)
{   
    uint16_t sum = a+b;
    return (sum < a) || (sum < b);
}

Skoro wartości logiczne są mapowane na "0" i "1", to nie potrzeba wprowadzać liczb.

1
andrzejlisek napisał(a):

Na pewno?

Na pewno.

1

Swoją drogą tutaj jest błąd:

Całość wydaje się znacząco przekomplikowana, jeśli chcesz policzyć carry to zrób to tak:

uint16_t carry(uint16_t a, uint16_t b)
{   
    uint16_t sum = a+b;
    return sum > std::max(a, b) ? 0 : 1;
}

https://wandbox.org/permlink/zfxmA8uHc25ZZt5Z

Powinno być:

uint16_t carry_hi(uint16_t a, uint16_t b)
{
    uint16_t sum = a + b;
    return sum >= std::max(a, b) ? 0 : 1;
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1