将排序的浮点数转换为按字母顺序排序的字符串

问题描述

我需要将 C++ 双精度数字转换为字符串,以便按字母顺序对字符串进行排序将提供与按算术排序数字相同的顺序。

我正在考虑使用固定大小的整数和小数部分。例如:

1.5 < 11.0,as well as alphabetically 0001.5000 < 0011.0000  

但是,这种方法有几个问题(例如范围限制)。有没有更好的方法?将 doubles 转换成 bitsets 是否符合要求?

解决方法

以下函数将“正常”双精度数转换为 <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,&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 result;
        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 result;
    }
    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
,

我们知道 FP 数由三部分组成:符号、指数、尾数。

符号必须在前,因为所有负数都排在+0.0之前

指数必须在下一个,因为所有指数为 N 的正数都排在指数 N+1 之前。自然地,指数的所有可能值都需要一个字符串表示,以便它们正确排序,但指数是一个整数。因此,零前缀有效。

尾数必须排在最后。它也是一个整数,所以同样的零前缀也有效。

NaN 是无序的,所以它会在哪里结束并不重要。如果您假设 IEEE754 (iec559) 表示法,+INF-INF 看起来可以正常工作。