【问题标题】:Cross platform/compiler consistent sprintf of floating point numbers浮点数的跨平台/编译器一致 sprintf
【发布时间】:2016-01-15 09:20:35
【问题描述】:

我们有一款游戏需要确定性,因为它是其多人模式的一部分。我们也使用Lua,内部使用sprintf(格式为%.14g)。

当它打印像 0.00001 这样的数字时会出现问题。在某些情况下,它会打印出1e-05,而在其他一些情况下,它会打印出1e-005(额外的零)。

例如,使用 Visual Studio 2015 编译时,它会打印 1e-005,而使用 Visual Studio 2013 编译时,它会打印 1e-05。我尝试了不同的语言环境设置,但似乎没有任何效果。

问题是:实现确定性结果的最佳解决方案是什么? 我真的不在乎科学记数法是标准化的还是被淘汰的。

我想到的解决方案:

  • 当我使用 %f 表示法时,它不会忽略不重要的零,因此使用 %.14f 会导致不切实际的长数字。
  • 使用自定义sprintf 方法(从一些标准库复制粘贴)
  • 使用了一些我没想到的特殊格式(我仅以此作为参考:http://www.cplusplus.com/reference/cstdio/printf/

【问题讨论】:

  • 我过去曾通过在缓冲区中使用sprintf 然后对其进行一些字符串处理来解决类似的问题。非常老套,但当时它对我有用。
  • 实现确定性结果的最佳解决方案是使用整数。但是你也可以使用 hexafloat %a 格式。
  • 我很关心它,因为确定性需要一直完整。任何差异都可能导致更大的差异等。我们还有用于尽快检测差异的控制机制,因此即使更改不会真正影响游戏,它也会提醒这些机制。 (类似于删除编译器警告)。
  • 所以你不希望它在某些机器上有一个额外的零,但它是供人类阅读的?修复似乎有点无意义。
  • 我希望它是 a) 人类可读 b) 在所有机器上都一样。

标签: c++ lua printf string-formatting


【解决方案1】:

您可以切换到LuaJIT。它在平台之间统一格式化数字。

来自extensions page

tostring() 等规范化 NaN 和 ±Inf

所有数字到字符串的转换在所有平台上始终将非有限数字转换为相同的字符串。 NaN 产生“nan”,正无穷大产生“inf”,负无穷大产生“-inf”。

tonumber() 等使用内置字符串到数字的转换

在所有平台上,所有字符串到数字的转换都一致地将整数和浮点输入转换为十进制和十六进制。 strtod() 不再使用,这避免了许多与糟糕的 C 库实现有关的问题。内置转换函数根据 IEEE-754 标准提供全精度,它独立于当前语言环境工作,并且支持十六进制浮点数(例如 0x1.5p-3)。

【讨论】:

  • 是的,这就是我们计划要做的事情。但不在稳定版本中:)
【解决方案2】:

投票最多的答案是错误的,因为文档首先是错误的。

这就是 LuaJIT 中正在发生的事情:

#define lua_number2str(s,n)sprintf((s),"%.14g",(n))
#define lua_str2number(s,p)strtod((s),(p))

查看tonumbertostring 实现,这些是为获取结果而调用的宏。

如果您找到真正的“内置”实现,请随时更正此答案,因为我也很想知道它是如何工作的。

【讨论】:

    【解决方案3】:

    一年后,我们就是这样解决的。

    我们下载了自定义打印实现(三重奏)并强制使用此实现而不是 lua(和我们的源代码)中的系统一。

    我们也不得不改变

    long double trio_long_double_t;
    

    double trio_long_double_t;
    

    在 triodef.h 中确保 Visual Studio 和 linux/mac 给出相同的结果。

    【讨论】:

      【解决方案4】:

      Lua 在标准库中为您提供 math.freexp。您可以使用它将浮点数拆分为指数和尾数形式,然后在不依赖于底层平台的纯 Lua 中进行自定义打印。这是一个例子:

      m,e = math.frexp(val)
      io.write(m)
      io.write('E')
      io.write(e)
      

      PS 享受周五的事实,让他们来吧 :)

      【讨论】:

      • 但这并不能解决这个问题,我想通过改变lua内部的C++实现来改变它,因为我不能强迫人们到处使用它。
      • 我以为你解决了这个问题 - 当我 1.5 年前回答时,你已经有了一个公认的答案。这是一个可以作为 lua 代码发布的解决方案 - 例如添加一个“标准”函数来格式化游戏中的解释器中的数字。或者,您可以通过更改 lua 实现来获得相同的效果。但它们都解决了在所有平台上获得确定性浮点输出的问题。
      猜你喜欢
      • 2014-01-24
      • 2014-09-28
      • 1970-01-01
      • 2017-11-15
      • 1970-01-01
      • 2013-04-08
      • 2010-10-29
      • 1970-01-01
      • 2015-06-10
      相关资源
      最近更新 更多