跨平台/编译器的浮点数sprintf一致性处理

我们有一个游戏需要确定性,因为它是多人游戏模型的一部分。我们还使用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/
点赞
用户646619
用户646619

你可以切换到Luajit。它在不同平台上格式化数字是一致的。

扩展页面

tostring()等规范化NaN和±Inf

所有数字转字符串的转换都在所有平台上将非有限数字转换为相同的字符串。 NaN会得到"nan",正无穷会得到"inf",负无穷会得到"-inf"。

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

所有字符串转数字的转换都在所有平台上一致的将十进制和十六进制的整数和浮点数转换。不再使用strtod(),这避免了由于C库实现问题而产生的众多问题。内置的转换函数根据IEEE-754标准提供完整的精度,它独立于当前区域设置并支持十六进制浮点数(例如0x1.5p-3)。

2015-10-16 17:44:59
用户383188
用户383188

Lua 在标准库中提供了 math.frexp 函数。你可以使用它将浮点数拆分为指数和尾数形式,然后在纯 Lua 中做自定义打印,而不依赖于底层平台。以下是一个示例:

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

PS:喜欢周五的事实,继续发!:)

2016-05-15 13:13:26
用户6826305
用户6826305

最被投票的答案是错误的,因为文档本身就是错误的。

这是在LuaJIT中发生的情况:

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

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

如果你找到了真正的“内置”实现方法,请随意纠正这个答案,因为我也很好奇它是如何实现的。

2016-09-13 11:43:00
用户478271
用户478271

一年后,我们解决了这个问题。

我们下载了自定义的打印实现(trio)并强制在 lua(以及我们的源代码)中使用这个实现,而不是使用系统提供的。

此外,我们还不得不在 triodef.h 中将

long double trio_long_double_t;

改为

double trio_long_double_t;

以确保 Visual Studio、Linux 和 macOS 得到相同的结果。


Note: 保留 markdown 格式

2016-10-04 12:53:45