Преобразование числа в строку и обратно

Я всегда использовал потоки, printf, string(x) или любой другой язык, который предлагался для преобразования числовых типов в строку или обратно. Однако я никогда не задумывался, как это делается на самом деле. Я искал в Google, но все результаты только для использования этих различных методов, а не для того, как преобразование действительно выполняется за кулисами :(

Для целых чисел, использующих двоичные, восьмеричные и шестнадцатеричные числа, кажется довольно простым, поскольку каждая «цифра» в строке представляет собой заданную группу битов (например, для двух шестнадцатеричных цифр я знаю ее xxxxyyyy), поэтому я мог бы сделать это с помощью битовых сдвигов и взять один цифра за раз, например, для шестнадцатеричной строки 0xFA20 значение равно "(15 ‹‹ 12) | (10 ‹‹ 8) | (2 ‹‹ 4) | (0 ‹‹ 0)".

Десятичные целые числа более сложны, поскольку основание 10 не соответствует основанию 2, как это, и поэтому один бит может влиять на более чем одну десятичную цифру, что делает преобразование в обоих направлениях более сложным...

Что касается чисел с плавающей запятой, я действительно понятия не имею. Я предполагаю, что целая и дробная части могут быть рассмотрены отдельно или что-то в этом роде? А как насчет экспоненты, заданного количества значащих цифр или заданного количества десятичных разрядов?


person Fire Lancer    schedule 23.08.2010    source источник
comment
Не ответ или что-то в этом роде, но этот вопрос заставил меня скривиться — однажды мне пришлось сделать это в Паскале, и это был абсолютный кошмар. Приходилось конвертировать в байтовую переменную потом в нужную. Абсолютный кошмар!   -  person Saladin Akara    schedule 23.08.2010


Ответы (3)


Десятичные преобразования немного медленнее, но не намного сложнее. Давайте посмотрим на шестнадцатеричное преобразование так, как если бы мы, вероятно, написали его в реальном коде. Например, в C++ вы можете сделать преобразование примерно так:

char digits[] = "0123456789abcdef";
std::string result;

int input = 0xFA20;

while (input) {
    int digit = input & 0xf; // or: digit = input % 0xf;
    input >>= 4;             // or: input /= 16;
    result.push_front(digits[digit]);
}

Однако прямо сейчас у этого есть некоторые магические числа. Избавимся от них:

const int base = 16;

while (input) { 
    int digit = input % (base - 1);
    input /= base;
    result.push_front(digits[digit]);
}

В процессе избавления от этих магических чисел мы также сделали подпрограмму почти универсальной — если мы изменим значение «базы», ​​остальная часть подпрограммы по-прежнему будет работать и преобразовывает входные данные в указанную базу. По сути, единственное другое изменение, которое нам нужно сделать, это добавить больше к массиву «digits», если мы хотим поддерживать базы больше 16.

Это также игнорирует несколько вещей для простоты. Совершенно очевидно, что если число отрицательное, вы обычно устанавливаете флаг, конвертируете в положительное число и в конце, если флаг был установлен, добавляете «-» в строку). С дополнением до 2 есть угловой случай для максимально отрицательного числа, которое нельзя преобразовать в положительное число (без преобразования в тип с большим диапазоном). Обычно вы справляетесь с этим, продвигая большинство типов. Для вашего самого большого целочисленного типа (который вы не можете продвигать) обычно проще всего просто жестко закодировать это одно значение.

В принципе, плавающая запятая не сильно отличается — вы по-прежнему в основном выполняете математические манипуляции, чтобы генерировать одну цифру за раз. На самом деле, это становится более сложным просто потому, что вам обычно приходится иметь дело с несколькими различными форматами (по крайней мере, с «базовым» форматом с плавающей запятой и каким-то «научным» форматом), а также с переменными для ширины поля и точности. К тому времени, когда вы с этим справитесь, у вас будет несколько сотен строк кода или около того — не особенно возмутительное количество, но, вероятно, немного больше, чем имеет смысл включать сюда.

person Jerry Coffin    schedule 23.08.2010
comment
При получении положительного значения целого числа всегда будет работать преобразование в беззнаковый тип того же размера. Например, для знакового 4-битного типа со значением -8 (1000 в дополнении к двойке) положительное значение равно 1000 -> 0111 -> 1000, что для беззнакового типа равно 8. - person Fire Lancer; 25.08.2010

Я искал в Google, но все результаты только для использования этих различных методов, а не для того, как преобразование действительно выполняется за кулисами :(

Из соображений производительности преобразование из одного представления в другое (особенно преобразования с плавающей запятой в целое число) часто является низкоуровневой инструкцией ЦП и реализуется на уровне процессора. Вот почему вы обычно не видите его повторно реализованным в библиотеках или на уровне языка.

Это особенно распространено в мире обработки сигналов, например, когда вы хотите взять сигнал и преобразовать его в дискретное целочисленное значение в некотором диапазоне.

person John Feminella    schedule 23.08.2010
comment
Я знаю, что у них есть вещи для float-›double, int-›float и т. д., но для строк я бы подумал, что convert_float_to_string(significant_figures=6) будет чем-то реализованным CRT или подобной библиотекой. В любом случае это не имеет отношения к моему вопросу, мне просто интересно, как вы логически выполняете форматирование/анализ строк. - person Fire Lancer; 23.08.2010

Для целых чисел можно найти остаток от деления, это последняя цифра, разделить на 10, найти модульный остаток - это предпоследняя цифра и так далее. Числа с плавающей запятой состоят из двух частей — значащих цифр и показателя степени, т.е. число = значащие.цифры * (основание ^ показатель степени), где основание может быть 10, 2 или другое число.

person Nickolay Olshevsky    schedule 23.08.2010