C++17 std::to_chars 和 std::from_chars 可移植性

C++17 std::to_chars and std::from_chars portability

提问人:MusicMaster 提问时间:4/16/2023 最后编辑:MusicMaster 更新时间:4/18/2023 访问量:222

问:

根据 cppreference,“仅当两个函数来自同一实现时,才能保证 std::to_chars/std::from_chars 可以完全恢复由 std::from_chars/std::to_chars 格式化的每个浮点值。

如果其中一个在不同的实现上运行,这可能会导致什么样的麻烦? 浮点数的哪些值(十进制和十六进制)会引起麻烦? 他们俩至少可以被信任,好吧,说“大多数”价值观,或者这只是一个不信任的不行吗?

[ 后续编辑: ]我坚持使用和.处理 所需的强制到/从“C”语言环境恢复,我使用了 每个线程等,不触及全局区域设置。 尽管有 1000 次操作,但速度似乎足够快,尽管没有正式测试。 我想知道区域设置调用是否是一个很大的速度? 我还了解到“0x”在 to/from_chars 中是被禁止的。我需要那个。strtodsnprintfuselocale

C++ C++17 标准

评论

1赞 aleck099 4/16/2023
github.com/llvm/llvm-project/issues/59374
1赞 doug 4/16/2023
每当浮点数的内部结构在实现之间不同时,表示一个实现所需的字符几乎总是与另一个不同。例如,如果这些结构使用相同的IEEE格式,那么它们很可能是兼容的,但不能保证。
0赞 MusicMaster 4/16/2023
感谢两者。道格,你回答了我的问题。我忽略了考虑所需的字符,甚至是十六进制字符。我想我指的是IEEE浮点数,也许我应该更新这个问题。
0赞 doug 4/16/2023
如果您需要验证系统之间的兼容性,您可以选择一大组随机浮点数,转换为字符,然后添加累积它们的摘要(哈希)并打印最终摘要。然后,只需在任何系统上运行代码,并查看摘要是否匹配。
0赞 Marek R 4/18/2023
youtu.be/kw-U6smcLzk

答:

0赞 doug 4/17/2023 #1

这是个好问题。可移植到后续编译器版本以及系统之间的数据交换需要一致性。因此,我编写了这个快速程序来生成 100,000 个随机的 32 位值。转换为浮点数,然后运行它们,然后运行它们。我从生成的所有字符中生成了一个 CRC16 值。如果这些在编译器或未来的编译器之间匹配,那么可以预期数据很可能是可移植的。此外,还测试了 to/from 转换会产生相同的浮点数,尽管这是标准中的要求。std::to_charsstd::from_chars

以下代码在 MSVC、CLang 和 GCC 中生成 crc=931c

#include <random>
#include <charconv>
#include <string>
#include <bit>
#include <iostream>

//#define CRC16
//#include "crc.h"

struct CRC16 {
    uint16_t state{ 0xffff };
    static constexpr uint16_t poly = 0x1021;
    uint16_t clk(char c)
    {
        state ^= c & 255;
        for (int i = 0; i < 8; i++)
        {
            if (state & 0x8000)
                state = (state << 1) ^ poly;
            else
                state = (state << 1);
        }
        return state;
    }
};


int main()
{
    CRC16 crc;
    constexpr uint32_t mask{ 0x7f800000 };
    std::mt19937 g(1);
    std::uniform_int_distribution<uint32_t> dist;

    // run 100,000 random bit float patterns and test for conversion consistency
    for (int i = 0; i < 100000; i++)
    {
        char result[100]{};
        uint32_t x = dist(g);
        // do not test NaN or De-normalized floats
        if (((x & mask) != mask) && (x & mask) != 0)
        {
            float f = std::bit_cast<float>(x);
            float fret;
            auto res1 = std::to_chars(&result[0], &result[100], f);
            for (char* p = &result[0]; p < res1.ptr; p++)
                crc.clk(*p);
            auto res2 = std::from_chars(&result[0], res1.ptr, fret);
            if (f != fret)
                throw "oops: mismatch";
        }
    }
    std::cout << std::hex << crc.state << '\n';
}

请注意,这仅测试浮点数。微小的修改也会测试双打。此外,如果这很关键,测试完整的浮点数集是可行的,但需要一段时间才能运行。Doubles 无法测试整个状态空间。