繁体   English   中英

QJsonDocument::toJson() 双精度不正确

[英]QJsonDocument::toJson() incorrect double precision

在我的项目中,我使用QJsonDocument::fromJson()从 json 文件中读取。 这很好用,但是当我尝试使用toJson()QJsonDocument写回文件时,一些双精度数已经搞砸了。

例如,对具有QJsonValue且 double 值为0.15的文档调用toJson()将保存到文件中为0.14999999999999999 我不想要这个。

这是因为 Qt 源文件qjsonwriter.cpp at line 126 (Qt 5.6.2)读取:

json += QByteArray::number(d, 'g', std::numeric_limits<double>::digits10 + 2); // ::digits10 is 15

最后那个+2把我弄乱了。 如果对QByteArray::number()的相同调用的精度为 15(而不是 17),则结果完全符合我的需要... 0.15

我了解浮点精度的格式如何导致双精度限制它可以表示的内容。 但是,如果我将精度限制为 15 而不是 17,这将具有匹配我想要的输入双精度的效果。

我怎样才能解决这个问题?

显然......我可以编写自己的 Json 解析器,但这是最后的手段。 显然我可以编辑 Qt 源代码,但是我的软件已经部署了 Qt5Core.dll,包含在每个人的安装目录中,而且我的更新程序并非旨在更新任何 dll。 所以我无法编辑 Qt 源代码。

手指交叉有人对此有一个神奇的解决方法:)

这具有匹配我想要的输入双精度的效果。

这个请求没有多大意义。 双精度型不包含有关精度的任何信息-它仅带有值。 0.15、0.1500和0.14999999999999999是完全相同的双精度值,并且JSON编写器无法知道首先如何从文件中读取它( 如果完全从文件中读取的 )。

通常,您不能像建议的那样要求最大15位精度,因为取决于特定值,精确的double-> text-> double往返最多需要17位,因此您将编写不正确的舍入值。 但是,某些JSON编写器所做的是用最小的小数位数编写数字, 以读取相同的double back 除非您-像许多人一样-进行从15到17的循环,否则很难正确地进行数字运算,以这种精度编写数字,将其解析回去,然后看是否返回为完全相同的double值。 尽管这会生成“更小(且更小)”的输出,但它的工作量更大,并且会降低JSON的写入速度,因此这就是Qt可能不这样做的原因。

不过,您仍然可以编写自己的JSON编写代码并具有此功能,对于简单的递归实现,我希望有15行代码。

话又说回来,如果您想精确地匹配您的输入,那将无法节省您-因为这是根本不可能的。

我也刚遇到这个。 我需要生成相对复杂的 json 对象以发送到一个 api,该 api 需要根据字段的不同精度/有效数字。 我没有用第三方库(或者我自己的库!)替换整个 Qt JSON 实现,而是拼凑了一个解决方案......

我与此相关的完整代码库过于广泛和详尽,无法在此处发布和解释。 但它的要点很简单。

我使用 QVariantMap(或 QVariantHash)来收集我的数据,然后通过内置的QJsonObject::fromVariantMapQJsonDocument::fromVariant函数将其转换为 json。 为了控制序列化,我定义了一个名为DataFormatOptions的类,它有一个decimalPrecision成员,然后我调用一个名为toMagicVar的函数来为我的数据结构创建“魔术变体”,以将其转换为 json 字节。 为了控制数字格式/精度, toMagicVar将双精度和浮点数转换为所需格式的字符串,并用一些“魔术字节”包围字符串值。 我的实际代码的编写方式可以轻松地在我正在构建的地图/哈希的任何“级别”上执行此操作/通过递归处理格式化,但我省略了这些细节......

const QString NO_QUOTE( "__NO_QUOT__" );

QVariant toMagicVar( const QVariant &var, const DataFormatOptions &opt )
{
...
    const QVariant::Type type( var.type() );
    const QMetaType::Type metaType( (QMetaType::Type)type );
... 
    if( opt.decimalPrecision != DataFormatOptions::DEFAULT_PRECISION
        && (type == QVariant::Type::Double || metaType == QMetaType::Float) )
    {
        static const char FORMAT( 'f' );
        const QString formatted( QString::number(
            var.toDouble(), FORMAT, opt.decimalPrecision ) );
        return QVariant( QString( NO_QUOTE + formatted + NO_QUOTE ) );
    }
... 
}

一旦我有了要作为QByteArray通过网络发送的 json 字节,我就会删除魔术字节,这样我用带引号的字符串表示的数字就会再次成为 json 中的数字。

// This is where any "magic residue" is removed, or otherwise manipulated,
// to produce the desired final json bytes...
void scrubMagicBytes( QByteArray &bytes )
{
    static const QByteArray
          QUOTE( "\"" )
        , NO_QUOTE_PREFIX( QUOTE + NO_QUOTE.toLocal8Bit() )
        , NO_QUOTE_SUFFIX( NO_QUOTE.toLocal8Bit() + QUOTE );
    static const ushort
        NO_QUOTE_PREFIX_LEN( NO_QUOTE_PREFIX.length() ),
        NO_QUOTE_SUFFIX_LEN( NO_QUOTE_SUFFIX.length() );
    for( int idx = bytes.indexOf( NO_QUOTE_PREFIX ); idx != -1;
             idx = bytes.indexOf( NO_QUOTE_PREFIX ) )
        bytes.remove( idx, NO_QUOTE_PREFIX_LEN );
    for( int idx = bytes.indexOf( NO_QUOTE_SUFFIX ); idx != -1;
             idx = bytes.indexOf( NO_QUOTE_SUFFIX ) )
        bytes.remove( idx, NO_QUOTE_SUFFIX_LEN );
}

上面的擦洗函数可能写得更简洁,如果你想将 QByteArray 转换为 QString 然后返回,以执行“全部替换”,QByteArray 似乎出于某种原因没有?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM