К чему относятся значения 48 и 87 при преобразовании шестнадцатеричного числа в десятичное в C?

Я пытаюсь понять процесс преобразования шестнадцатеричного числа в его десятичный эквивалент, в частности, при преобразовании каждой шестнадцатеричной цифры в ее десятичное значение.

Скажем, когда цифра i в hexVal равна любым символам в диапазоне от «0» до «9», ее decVal равняется hexVal, вычтенному из 48, а затем рассчитанному по времени с помощью digitBase:

if ((hexVal[i] >= '0') && (hexVal[i] <= '9')) {
    decVal += (hexVal[i] - 48) * digitBase;
    ...
}

Я понимаю, что 48 - это значение ASCII, равное «0». В чем я сомневаюсь, так это в том, откуда берутся значения 55 и 87, когда цифра i в hexVal равна диапазонам от «A» до «F» и от «a» до «f»:

else if ((hexVal[i] >= 'A') && (hexVal[i] <= 'F')) {
    hexToDec += (hexVal[i] - 55) * digitBase;
    ...
}

и

else if ((hexVal[i] >= 'a') && (hexVal[i] <= 'f')) {
    hexToDec += (hexVal[i] - 87) * digitBase;
    ...
}

Приведенные выше блоки кода извлечены из следующей функции, которая хорошо работает для преобразования шестнадцатеричных чисел в эквивалентные им десятичные.

int conv_hex_to_dec(char hexVal[]) {

    int hexToDec = 0;
    int len = strlen(hexVal);
    int digitBase = 1; 

    // Extract hex characters as digits from last character
    for (int i = len - 1; i >= 0; i--) {

        if ((hexVal[i] >= '0') && (hexVal[i] <= '9')) {
            hexToDec += (hexVal[i] - 48) * digitBase;
            digitBase = digitBase * 16;
        }

        else if ((hexVal[i] >= 'A') && (hexVal[i] <= 'F')) {
            hexToDec += (hexVal[i] - 55) * digitBase; 
            digitBase = digitBase * 16;
        }
        else if ((hexVal[i] >= 'a') && (hexVal[i] <= 'f')) {
            hexToDec += (hexVal[i] - 87) * digitBase; 
            digitBase = digitBase * 16;
        }
        else {
            printf("Invalid hex val");
        }
    }

    return hexToDec;
}

Любое объяснение будет высоко оценено.

Спасибо.


person dtusllitg    schedule 20.05.2020    source источник
comment
asciitable.com И именно поэтому мы не должны использовать магическое число 48, а вместо этого набирать '0'.   -  person Lundin    schedule 20.05.2020
comment
Это преобразование строки в шестнадцатеричном представлении в целое число. Вычитание значения ascii для получения фактического десятичного значения символа. то есть 9 - это значение ascii '9' - '0'   -  person Tony Tannous    schedule 20.05.2020
comment
следующая функция, которая хорошо работает для преобразования шестнадцатеричных чисел в эквивалентные им десятичные ---› только до INT_MAX. Кроме того, код UB.   -  person chux - Reinstate Monica    schedule 20.05.2020


Ответы (2)


48 — код ASCII для «0»; коды ASCII для «A» и «a» равны 65 (55 = 65–10) и 97 (87 = 97–10) соответственно.

person Scott Hunter    schedule 20.05.2020

Это яркий пример того, почему «магические числа» в коде — это плохо.

Этот код предполагает, что используется кодировка символов ASCII. Соответствующие цифры:

  • 48: код ASCII для '0'
  • 65: код ASCII для 'A'
  • 97: код ASCII для 'a'

Итак, в этой строке кода:

decVal += (hexVal[i] - 48) * digitBase;

Он вычитает код ASCII для hexVal[i] из кода ASCII для '0'. Это преобразует символ в диапазоне от '0' до '9' в значение в диапазоне от 0 до 9.

Точно так же это:

hexToDec += (hexVal[i] - 55) * digitBase;

Вычитание кода ASCII hexVal[i] из кода ASCII 'A' и добавление 10. Это преобразует символ в диапазоне от 'A' до 'F' в значение в диапазоне от 10 до 15. Аналогично проверке с 87, это преобразует символ в диапазоне от 'a' до 'f' до значения в диапазоне от 10 до 15.

Лучше написать это так:

    if ((hexVal[i] >= '0') && (hexVal[i] <= '9')) {
        hexToDec += (hexVal[i] - '0') * digitBase;
        digitBase = digitBase * 16;
    }

    else if ((hexVal[i] >= 'A') && (hexVal[i] <= 'F')) {
        hexToDec += (hexVal[i] - 'A' + 10) * digitBase; 
        digitBase = digitBase * 16;
    }
    else if ((hexVal[i] >= 'a') && (hexVal[i] <= 'f')) {
        hexToDec += (hexVal[i] - 'a' + 10) * digitBase; 
        digitBase = digitBase * 16;
    }

Так как читателю более понятно, каково намерение.

person dbush    schedule 20.05.2020
comment
Спасибо за подробное объяснение и за указание на хорошую практику кодирования. - person dtusllitg; 21.05.2020
comment
Это предполагает, что коды символов для 0-9 и af расположены последовательно, что, вероятно, верно, но может и не быть (рассмотрите EBCDIC, который имеет разрыв в кодах для букв, хотя и не в аф). - person Scott Hunter; 21.05.2020