【问题标题】:Parsing floats in Rust from Fortran formats从 Fortran 格式解析 Rust 中的浮点数
【发布时间】:2015-12-28 07:56:17
【问题描述】:

我正在用 Rust 重写一个 C++ 解析器以获取旧的 ASCII 数据格式。这种格式的实数值允许以任何 Fortran 识别的格式存储。不幸的是,Fortran 可以识别 Rust(或大多数其他语言)无法识别的某些格式。例如,值 101.01 可能表示为

  • 101.01
  • 1.0101E2
  • 101.01e0
  • 101.01D0
  • 101.01d0
  • 101.01+0
  • 1010.1-1

前三个都是 Rust 原生识别的。其余四个构成挑战。在 C++ 中,我们使用以下例程来解析这些值:

double parse(const std::string& s){
  char* p;
  const double significand = strtod(&s[0], &p);
  const long exponent = (*p == '\0') ? 
                          0 : isalpha(*p) ?
                            strtol(p+1, nullptr) :
                              strtol(p, nullptr);
  return significand * pow(10, exponent);
}

查看 Rust 文档后,标准库似乎没有像 strtodstrtol 那样提供部分字符串解析。出于性能原因,我想避免对字符串进行多次传递或使用正则表达式。

【问题讨论】:

    标签: floating-point rust


    【解决方案1】:

    这本来是对 Veedrac 的回答的评论,但评论有点长。

    正如 Veedrac 解释的那样,准确地解析浮点数是困难。标准库中的实现是完全准确的,并且优化得相当好。特别是,对于天真的算法起作用的大多数输入,它并不比天真的不准确算法慢多少。你应该使用它。完全免责声明:我写的。

    我不同意 Veedrac 的地方是,如果您想重用该代码,该如何进行。从标准库中删除它是一个坏主意。它非常庞大,大约有 2.5k 行代码,并且它仍然偶尔会更改/改进——尽管很少而且大部分是以非常小的方式进行的。但总有一天我会找到时间重写缓慢的路径以变得更好更快,承诺。 如果您撕掉了这段代码,您将不得不采用core::num::dec2flt 模块并修改parse 子模块以识别其他指数。当然,您不会自动从未来的改进中受益,如果您对性能感兴趣,那就太可惜了。

    最明智的方法是将其他格式转换为 Rust 支持的格式。如果它是 dD 或裸 +,您可以简单地将其替换为 e 并将其传递给 string 。只有在1010.1-1 的情况下,您才需要插入e 并移动字符串的指数部分。这不应该花费太多性能。浮点字符串很短(最多 20 个字节左右,通常更少),并且实际的转换工作在每个字节中完成了大量的工作。对于您的 C++ 代码也是如此,因为 strtod is accurate in glibc too.或者至少它正在尝试这样做,它无法修复围绕它构建的 ad hoc 算法。无论如何,它正在尝试。

    另一种可能性是使用 FFI 调用 C 的strtod。使用libc crate 并致电libc::strtod。这需要一些扭曲才能从&str 转换为指向c_char 的原始指针,并且它会糟糕地处理内部0 字节,但是您显示的代码无论如何都不是非常健壮。这将允许您将算法转换为具有相同性能和语义以及(不)准确性的 Rust。

    【讨论】:

    • 我提到了翻译的想法,但没有按下它,因为 apmccartney 明确表示它太慢了。我反对“它并没有比天真的不准确算法慢多少”的说法;我刚刚启动了一个快速不准确的测试解析器,它在“33.1453326”(以及其他一些测试用例)上的速度是原来的 5 倍。如果他在这里担心速度(假设他不仅仅是为了它而说的话),那几乎是模棱两可的。在此之上添加 extra 解析只会加剧差异,一点点性能调整也会如此。
    • 目前,我已经实施了 delnan 建议的“最明智的方式”(如果只是希望未来几年数据会有所改善)。如果这种方法被证明过于昂贵,我可能会实施你在 cmets 中建议的策略来回应你的回应,Veedrac。
    • @Veedrac 根据我自己的基准与标准库中的旧实现(几个月前),我预计该类输入的速度大约是 2 倍。 5x 让我感到惊讶,你能把你的代码放在一个要点中以便我研究吗?
    • @delnan 这不是很漂亮(每次我试图让它变得漂亮,它最终都会变慢),但是I've finished it up for you。我不知道它有多正确,因为我几乎没有对其进行测试,但它似乎大部分都有效。
    【解决方案2】:

    您在 C++ 中的示例没有给出完全准确的结果,但 Rust 的浮点解析 is intended to be perfectly accurate, and as such has slower parsing than you might need

    如果您手动实现近似解析,它可能会比任何其他可用技术更快。我在本地进行的一项快速测试表明,您可以轻松地将标准库的 parse 方法的性能提高 5 倍。

    如果您希望进行精确解析,那么您的 C++ 代码是不够的。预解析(例如,使用 Regex)可能是最简单的方法,但您也可以从标准库中提取代码并对其进行修改。

    【讨论】:

    • 感谢您的警告!我知道我发布的算法会引入错误;也就是说,我有幸使用代表测量的数据,其中七位数的精度被认为是相当好的。在我们的测试过程中没有观察到任何引入的错误。
    • 也感谢您对 github 提交的引用。它使阅读变得有趣。
    • 如果您只有 7 位数的精度并且需要速度,您只需将值解析为 i64 并乘以缓存的 10 幂(计算指数为将小数位添加到显式位)。这应该非常快。
    • 我已将此回复添加为书签以供将来参考。该应用程序旨在用于实时处理,但我们仍处于负载测试的早期阶段。如果发现文件解析是瓶颈,我会实现这个方法进行比较。
    • 我们最终使用了您在此处的 cmets 中描述的技术。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多