跨平台/编译器一致的浮点数的sprintf。

时间:2022-05-31 02:37:42

We have a game that needs to be deterministic as it is part of its multiplayer model. We also use Lua, which uses the sprintf internally (the format is %.14g).

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

The problem arises when it prints number like 0.00001. In some cases it prints 1e-05 and in some other cases, it prints 1e-005 (extra zero).

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

For example when compiled with Visual studio 2015 it prints 1e-005, and with Visual studio 2013 it prints 1e-05. I tried different locale settings, but it doesn't seem to have any effect.

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

The question is: What is the best solution to achieve deterministic results? I don't really care if the scientific notation is standardized, or eliminated.

问题是:达到确定性结果的最佳解决方案是什么?我并不关心科学符号是标准化的还是被淘汰的。

Solutions I thought about:

我想到的解决方案:

  • When I use the %f notation, it doesn't ignore the insignificant zeroes, so having %.14f would result into impractically long numbers.
  • 当我使用%f符号时,它不会忽略无关紧要的零,所以有%。14f会导致不实际的长数字。
  • Using custom sprintf method (copy pasted from some of the standard libraries)
  • 使用自定义sprintf方法(从一些标准库中复制粘贴)
  • Using some special format I didn't thought about (I use only this as reference: http://www.cplusplus.com/reference/cstdio/printf/)
  • 使用一些我没有考虑过的特殊格式(我只使用这个作为参考:http://www.cplusplus.com/reference/cstdio/printf/)

4 个解决方案

#1


18  

You can switch to LuaJIT. It formats numbers consistently between platforms.

您可以切换到LuaJIT。它在不同的平台之间格式化数字。

From the extensions page:

从扩展页面:

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".

在所有平台上,所有数字到字符串的转换都一致地将非有限的数字转换为相同的字符串。NaN导致“NaN”,正无穷导致“inf”,负无穷导致“-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).

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

#2


4  

The most voted answer is wrong, because the documentation is wrong in first place.

大多数投票的答案是错误的,因为文档一开始就是错误的。

This is what's happening in LuaJIT:

这就是卢埃特发生的事情:

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

Looking at tonumber and tostring implementation those are the macros called to get the results.

查看tonumber和tostring实现,这些是用于获取结果的宏。

Feel free to correct this answer if you find the real "built-in" implementation, cause I'm curious as well to know how it really works.

如果您找到真正的“内置”实现,请随时纠正这个答案,因为我也很好奇它是如何工作的。

#3


3  

A year later, this is how we solved it.

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

We downloaded custom print implementation (trio) and forced usage of this implementation instead of the system one in the lua (and our sources).

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

We also had to change

我们也必须改变

long double trio_long_double_t;

to

double trio_long_double_t;

in the triodef.h to ensure the Visual studio and linux/mac gives the same results.

triodef。h确保Visual studio和linux/mac提供相同的结果。

#4


1  

Lua gives you math.frexp in the standard library. You can use this to split your floats into exponent and mantissa form, and then do a custom print in pure Lua that will not depend on the underlying platform. Here is an example:

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

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

PS Enjoying the Friday Facts, keep 'em coming :)

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

#1


18  

You can switch to LuaJIT. It formats numbers consistently between platforms.

您可以切换到LuaJIT。它在不同的平台之间格式化数字。

From the extensions page:

从扩展页面:

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".

在所有平台上,所有数字到字符串的转换都一致地将非有限的数字转换为相同的字符串。NaN导致“NaN”,正无穷导致“inf”,负无穷导致“-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).

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

#2


4  

The most voted answer is wrong, because the documentation is wrong in first place.

大多数投票的答案是错误的,因为文档一开始就是错误的。

This is what's happening in LuaJIT:

这就是卢埃特发生的事情:

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

Looking at tonumber and tostring implementation those are the macros called to get the results.

查看tonumber和tostring实现,这些是用于获取结果的宏。

Feel free to correct this answer if you find the real "built-in" implementation, cause I'm curious as well to know how it really works.

如果您找到真正的“内置”实现,请随时纠正这个答案,因为我也很好奇它是如何工作的。

#3


3  

A year later, this is how we solved it.

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

We downloaded custom print implementation (trio) and forced usage of this implementation instead of the system one in the lua (and our sources).

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

We also had to change

我们也必须改变

long double trio_long_double_t;

to

double trio_long_double_t;

in the triodef.h to ensure the Visual studio and linux/mac gives the same results.

triodef。h确保Visual studio和linux/mac提供相同的结果。

#4


1  

Lua gives you math.frexp in the standard library. You can use this to split your floats into exponent and mantissa form, and then do a custom print in pure Lua that will not depend on the underlying platform. Here is an example:

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

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

PS Enjoying the Friday Facts, keep 'em coming :)

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