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

#c #c 11

Вопрос:

Мне нужно преобразовать числа C с двойной точностью в строки таким образом, чтобы сортировка строк в алфавитном порядке обеспечивала тот же порядок, что и арифметическая сортировка чисел.

Я рассматриваю возможность использования целого числа и десятичной части фиксированного размера. Например:

 1.5 < 11.0, as well as alphabetically 0001.5000 < 0011.0000  
 

Однако у этого метода есть несколько проблем (таких как ограничение диапазона). Есть ли какой-нибудь лучший метод? Соответствует ли преобразование двойников в наборы битов требованиям?

Комментарии:

1. Как вы представляете огромные числа в своей строке, в экспоненциальной нотации или полностью заполненной, например, будет ли 1E100 строкой длиной 100 символов или просто «1E100» или «1.0 E 100» или ???

2. @franji1 Я не смогу представить огромные числа с помощью моего базового подхода.

3. Хотя это шутка RFC, мне интересно, описывают ли методы, описанные в tools.ietf.org/html/rfc2550 это помогло бы вам 🙂

4. Будет ли приемлемым шестнадцатеричный формат с плавающей запятой ?

5. Можете ли вы предположить std::numeric_limits<double>::is_iec559 ? Это упростило бы ответ.

Ответ №1:

Следующая функция преобразует «обычные» двойники в строку в форме <sign><exponent><mantissa> , где знак либо N (отрицательный), либо P (положительный). Точность мантиссы установлена на 17 десятичных знаков и может быть легко скорректирована.

Особые случаи (включая 0) обрабатываются в нижней части функции.

Обновление: Добавлено изменение порядка отрицательных чисел. Поскольку отрицательные числа сначала нужно отсортировать по наибольшему абсолютному значению, мы должны построить дополнение 9 к показателю и мантиссе. Кроме того, отрицательные показатели должны следовать за положительными показателями для отрицательных чисел. Следовательно, строчная буква «n» используется для отрицательных показателей для отрицательных чисел, так как она идет после прописной буквы «P» по ее значению ASCII.

 #include <iostream>
#include <iomanip>
#include <sstream>
#include <cmath>
#include <cctype>

std::string doubleToString(double x)
{
    if(std::isnormal(x))
    {
        int e;
        double m = std::frexp(x, amp;e);
        std::stringstream s;
        bool negative = (m < 0.0);
        s << (negative ? "N" : "P"); // "N" for negative; "P" for positive
        s << "E"; // Exponent
        if(e >= 0)
        {
            s << "P" << std::setfill('0') << std::setw(5) << e;
        }
        else
        {
            s << ((negative) ? "n" : "N") << std::setfill('0') << std::setw(5) << -e;
        }
        s << "M" << std::setprecision(17) << std::fixed << std::fabs(m); // Mantissa
        std::string resu<
        if(negative)
        {   // Convert numbers to 9's complement
            std::string tmp = s.str();
            for(auto c = tmp.begin(); c != tmp.end(); c  )
            {
                if(isdigit(*c))
                {
                    result  = ('9' - ((*c) - '0'));
                }
                else
                {
                    result  = *c;
                }
            }
        }
        else
        {
            result = s.str();
        }
        return resu<
    }
    else
    {
        switch(std::fpclassify(x))
        {
            case FP_NAN: return "xNaN";
            case FP_ZERO: return "O";  // "O" is placed between "N" (negative) and "P" (positive)
            case FP_INFINITE: return (x>0) ? "PInf":"N Inf"; // The space after "N" goes before any number
            case FP_SUBNORMAL: // Fall through
            default:
                return "xUnknown";
        }
    }
}
 

Примеры результатов:

 1.500000:  PEP00001M0.75000000000000000                                                                                                   
0.500000:  PEP00000M0.50000000000000000                                                                                                   
-0.005000: NEn99992M9.35999999999999998                                                                                                  
-0.500000: NEP99999M9.49999999999999999                                                                                                  
0.005000:  PEN00007M0.64000000000000001                                                                                                   
0.000000:  O                                                                                                                              
3.141593:  PEP00002M0.78539816339744828                                                                                                   
inf:       PInf                                                                                                                                
-inf:      N Inf                                                                                                                              
nan:       xNaN
 

Ответ №2:

Мы знаем, что числа FP состоят из трех частей: знака, показателя, мантиссы.

Знак должен стоять на первом месте, потому что все отрицательные числа сортируются до 0.0

Следующим должен быть показатель степени, потому что все положительные числа с показателем N сортируются перед показателем N 1. Естественно, все возможные значения показателя степени нуждаются в строковом представлении, чтобы они были упорядочены правильно, но показатель степени является целым числом. Таким образом, работает нулевая приставка.

Мантисса должна быть последней. Это также целое число, так что работает та же самая нулевая приставка.

NaN неупорядочен, поэтому не имеет значения, где он окажется. INF и -INF похоже, что они будут работать, если вы примете представления IEEE754 (iec559).

Комментарии:

1. Одна деталь: в ASCII все идет раньше - , а нам нужно обратное. Возможно, переключение и - для P и M соответственно может быть вариантом. Кроме того, показатель степени является целым числом со знаком.

2. @nielsen: Я не упоминал, как вы будете представлять поля, за исключением возможного заполнения нуля. В качестве примера вы также можете взять кодировку Base-64 двоичного представления IEEE754 double . В нем есть поля в том порядке, о котором я упоминал, а также с фиксированной шириной.

3. Заполнение нуля не будет применимо для кодировки базового 64 значения IEEE754, если это то, к чему мы стремимся. В любом случае, кодировка Base-64 будет иметь ту же проблему со знаком, поскольку 0 является положительным, а 1 отрицательным, т. е. Отрицательные числа будут следовать за положительными числами. Поэтому первый бит должен быть перевернут.