#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 отрицательным, т. е. Отрицательные числа будут следовать за положительными числами. Поэтому первый бит должен быть перевернут.