交叉platform/compiler浮点数的一致sprintf
Cross platform/compiler consistent sprintf of floating point numbers
我们有一款游戏需要确定性,因为它是多人游戏模型的一部分。我们还使用了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/)
您可以切换到 LuaJIT。它在平台之间以一致的方式格式化数字。
tostring() etc. canonicalize NaN and ±Inf
All number-to-string conversions consistently convert non-finite numbers to the same strings on all platforms. NaN results in "nan", positive infinity results in "inf" and negative infinity results in "-inf".
tonumber() etc. use builtin string to number conversion
All string-to-number conversions consistently convert integer and floating-point inputs in decimal and hexadecimal on all platforms. strtod() is not used anymore, which avoids numerous problems with poor C library implementations. The builtin conversion function provides full precision according to the IEEE-754 standard, it works independently of the current locale and it supports hex floating-point numbers (e.g. 0x1.5p-3).
Lua 在标准库中为您提供 math.frexp。您可以使用它将浮点数拆分为指数和尾数形式,然后以不依赖于底层平台的纯 Lua 进行自定义打印。这是一个例子:
m,e = math.frexp(val)
io.write(m)
io.write('E')
io.write(e)
PS 享受周五的事实,让他们来:)
投票最多的答案是错误的,因为文档首先是错误的。
这就是 LuaJIT 中正在发生的事情:
#define lua_number2str(s,n)sprintf((s),"%.14g",(n))
#define lua_str2number(s,p)strtod((s),(p))
查看 tonumber
和 tostring
实现,这些是为获得结果而调用的宏。
如果您找到真正的 "built-in" 实现,请随时更正此答案,因为我也很好奇它是如何工作的。
一年后,我们就是这样解决的。
我们下载了自定义打印实现(三重奏)并强制使用此实现而不是 lua(和我们的来源)中的系统。
我们也不得不改变
long double trio_long_double_t;
至
double trio_long_double_t;
在 triodef.h 中确保 Visual studio 和 linux/mac 给出相同的结果。
我们有一款游戏需要确定性,因为它是多人游戏模型的一部分。我们还使用了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/)
您可以切换到 LuaJIT。它在平台之间以一致的方式格式化数字。
tostring() etc. canonicalize NaN and ±Inf
All number-to-string conversions consistently convert non-finite numbers to the same strings on all platforms. NaN results in "nan", positive infinity results in "inf" and negative infinity results in "-inf".
tonumber() etc. use builtin string to number conversion
All string-to-number conversions consistently convert integer and floating-point inputs in decimal and hexadecimal on all platforms. strtod() is not used anymore, which avoids numerous problems with poor C library implementations. The builtin conversion function provides full precision according to the IEEE-754 standard, it works independently of the current locale and it supports hex floating-point numbers (e.g. 0x1.5p-3).
Lua 在标准库中为您提供 math.frexp。您可以使用它将浮点数拆分为指数和尾数形式,然后以不依赖于底层平台的纯 Lua 进行自定义打印。这是一个例子:
m,e = math.frexp(val)
io.write(m)
io.write('E')
io.write(e)
PS 享受周五的事实,让他们来:)
投票最多的答案是错误的,因为文档首先是错误的。
这就是 LuaJIT 中正在发生的事情:
#define lua_number2str(s,n)sprintf((s),"%.14g",(n))
#define lua_str2number(s,p)strtod((s),(p))
查看 tonumber
和 tostring
实现,这些是为获得结果而调用的宏。
如果您找到真正的 "built-in" 实现,请随时更正此答案,因为我也很好奇它是如何工作的。
一年后,我们就是这样解决的。
我们下载了自定义打印实现(三重奏)并强制使用此实现而不是 lua(和我们的来源)中的系统。
我们也不得不改变
long double trio_long_double_t;
至
double trio_long_double_t;
在 triodef.h 中确保 Visual studio 和 linux/mac 给出相同的结果。