1

I have a task like this: The user enters the numbers N1(str1) and N2(str2) in hexadecimal. The program must convert the numbers from hexadecimal to a system of 2 ^ 16 and count the sum of the numbers N1 and N2 in the 2^16 system, then translate the result into a hexadecimal system.

I had such an idea: first convert from hexadecimal to decimal (I can do this). Then take each number modulo 2 ^ 16 the logarithm of the base 2 ^ 16 of the number N1dec(dec11) (or N2dec(dec22)) times and write the remainders in the corresponding arrays. This is where my problems began. My conversion from decimal to 2^16 system does not work. Hope You can help.

#include <iostream> 
using namespace std;

int main()
{
//HEX to decimal
const char* const str1 = "101D0";//7A120 = 500000; 101D0 = 66000;   //1F4 = 500=dec1=N1
cout << "Hello!\nFirst number in HEX system is " << str1 << endl;
istringstream is(str1);
int dec1;
is >> hex >> dec1;
if (!is && !is.eof()) throw "dammit!";
cout << "First number in decimal system: " << dec1 << endl;
const char* const str2 = "1567";//5479=dec2=num2
cout << "Second number in HEX system is " << str2 << endl;
istringstream iss(str2);
int dec2;
iss >> hex >> dec2;
if (!iss && !iss.eof()) throw "dammit!";
cout << "Second number in decimal system: " << dec2 << endl;
//

//Decimal to 2^16 system
int dec11 = dec1;//because dec11 will be = 0
int dec22 = dec2;//because dec22 will be = 0

int k = 1 << 16;
cout << "2^16 = " << k << endl;
int intPART1 = log(dec11) / log(k);
cout << "Int part of log2^16 (" << dec11 << ") is " << intPART1 << endl << "So num1 in 2^16 system will look like ";

int *n1 = new int[intPART1 + 1];
for (int i = 0; i <= intPART1; i++)
{
    if (i != 0)
    {
        n1[i] = dec11 % k*(1<<16-1);
        dec11 = dec11 / k;
    }
    else
    {
        n1[i] = dec11 % k;
        dec11 = dec11 / k;
    }
}
for (int i = intPART1; i >= 0; i--)
{
    cout << n1[i] << "   ";
}
cout << endl;
int intPART2 = log(dec22) / log(k);
cout << "Int part of log2^16 (" << dec22 << ") is " << intPART2 << endl << "So num2 in 2^16 system will look like ";

int *n2 = new int[intPART2 + 1];
for (int i = 0; i <= intPART2; i++)
{
    if (i != 0)
    {
        n2[i] = dec22 % k*(1 << 16 - 1);
        dec22 = dec22 / k;
    }
    else
    {
        n2[i] = dec22 % k;
        dec22 = dec22 / k;
    }
}

for (int i = intPART2; i >= 0; i--)
{
    cout << n2[i] << "   ";
}
cout << endl;
Laura
  • 35
  • 4
  • That has nothing to do with my comment. [`std::stoi`](https://en.cppreference.com/w/cpp/string/basic_string/stol) converts a string to an int and you can specify the base of that string. – Timo Oct 01 '19 at 22:22
  • 1
    By *system of 2^16* you mean a number of base 65536? – Timo Oct 01 '19 at 22:25
  • @Timo Yes, a number of base 65536 – Laura Oct 01 '19 at 22:34
  • 1
    "base 65536" - a single digit can have 65535 values?? You would write 1000000 as two digits: 15 decimal digit and 16960 ones digit? – KamilCuk Oct 01 '19 at 23:20
  • Conversion should be way more straight-forward than what you're attempting. Each base-2^16 digit is represented by exactly 4 base-16 digits. – paddy Oct 01 '19 at 23:25
  • Fix your indentation. Also, your code has memory leaks. – Phil1970 Oct 01 '19 at 23:44

2 Answers2

2

Since hexadecimal values are of base 16, let us say 16^1 and base 2^16 can be recalculated to 16^4 we can already see that your target base is a multiple of your source base. This makes the computation pretty easy and straight forward. All we have to do is some bit shifiting.

int hexToInt(char c)
{
    if (c >= 'a')
        return c - 'a' + 10;
    if (c >= 'A')
        return c - 'A' + 10;
    return c - '0';
}

// Converts hex to base 2^16. vector[0] holds the MSB.
std::vector<unsigned short> toBase0x10000(std::string const& hex)
{
    std::size_t bufSize = hex.size() / 4 + (hex.size() % 4 > 0);
    std::vector<unsigned short> number(bufSize);

    int shift = 0;
    int value = 0;
    std::size_t numIndex = number.size();

    for (int i = hex.size() - 1; i >= 0; i--)
    {
        value |= hexToInt(hex[i]) << shift;
        shift += 4;

        if (shift == 16)
        {            
            number[--numIndex] = static_cast<unsigned short>(value);
            shift = 0;
            value = 0;
        }
    }

    if (value != 0)
        number[--numIndex] = static_cast<unsigned short>(value);

    return number;
}

std::string fromBase0x10000(std::vector<unsigned short> const& num)
{
    std::stringstream ss;
    for (auto&& digit : num)
        ss << std::hex << digit;
    return ss.str();
}

toBase0x10000 returns a std::vector<unsigned short> so each element in the vector represents one digit of your base 2^16 number (since unsigned short can hold exaclty that value range).

As a side effect this implementation supports any precision number so you are not limited by the value range of numeric types like int or long.

Here is a full example.

Timo
  • 9,269
  • 2
  • 28
  • 58
  • Why did you write std::size_t bufSize = hex.size() / 4 + (hex.size() % 4 > 0); instead of std::size_t bufSize = hex.size(); ? – Laura Oct 02 '19 at 19:09
  • And how does it (hex.size ()% 4> 0) work? This is not a comparison of the left side with zero? – Laura Oct 02 '19 at 19:17
  • What is the difference between "a" and "A" and why do we need to return c - 'a' + 10? If I have the number 1F4 why 4 - 'A' = -13, not -6, when in the hexadecimal system 'A' it is '10' in decimal. I have an assumption that this is due to ASCII tables, but I don’t understand how. Explain to me please. – Laura Oct 02 '19 at 20:09
  • @Laura The `hex.size() / 4` part is because the vector is of type `unsigned short` which is 16bit. A hex character (0-F) can fit exactly into 4 bits, so there is a 1/4 ratio between those two types. However the 1/4 ratio also has the problem that it doesn't always procude a round number. Ex. "123456789" has 9 characters. If we divide that by 4 we get 2.25 which integer arithmetics will cut to 2. That is where `+ (hex.size() % 4 > 0)` comes into play. It checks if the string's size is divisible by 4 and if not, adds 1 to `bufSize` (otherwise the expression is 0). – Timo Oct 02 '19 at 20:42
  • `(hex.size() % 4 > 0)` is a boolean expression. And in arithmetics `true` translates to 1 and `false` to 0. – Timo Oct 02 '19 at 20:43
  • The `hexToInt` function is straight forward. The formula is tied to the ASCII table, because 0...9, A...F and a...f are at different places in the ASCII table so we have to handle each case separatly. Your example is wrong, "1F4" is resolved like this: for '1' the last return statement applies: '1' - '0' = 1. For 'F', the 2nd return applies: 'F' - 'A' + 10 = 15 and for '4' the last applies again: '4' - '0' = 4. Note that this function is called for each character in the string individually. – Timo Oct 02 '19 at 20:50
0

Since this looks like a learning exercise you want to solve yourself, here are two hints.

A hex digit represents four bits, so each base-65,536 digit consists of four hex digits. You can therefore read the digits in groups of four, with no need to convert to or from decimal. The same algorithm you learned to decode four decimal digits will work for hex, except the multiplications will be even more efficient because the compiler will optimize them into left-shift instructions.

You should use the uint16_t type from <stdint.h> for this arithmetic, as it it is exactly the right size and unsigned. Unsigned arithmetic overflow is defined as wrapping around, which is what you want. Signed overflow is undefined behavior. (Or #include <cstdint> followed by using std::uint16_t; if you prefer.)

To add digits in any base b, take the sum of the digits modulo b. This will be even easier when b is a power of 2, because the x86 and many other CPUs have a 16-bit unsigned add instruction that does this in hardware, and on any machine that doesn’t, the compiler can optimize this to the bitmask & 0xFFFFU.

In both cases, you can, if you want, write out the binary optimizations by hand using << and & rather than * and %. This might even improve the generated code, slightly, if you use signed rather than unsigned math. However, any modern compiler is smart enough to perform this kind of micro-optimization for you. You are better off not optimizing prematurely, and writing code that is easier to read and understand.

Davislor
  • 14,674
  • 2
  • 34
  • 49