Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cross platform/compiler consistent sprintf of floating point numbers

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

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

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.

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.
  • Using custom sprintf method (copy pasted from some of the standard libraries)
  • Using some special format I didn't thought about (I use only this as reference: http://www.cplusplus.com/reference/cstdio/printf/)
like image 964
kovarex Avatar asked Oct 16 '15 14:10

kovarex


3 Answers

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

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

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

like image 131
Colonel Thirty Two Avatar answered Oct 04 '22 11:10

Colonel Thirty Two


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.

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.

like image 26
D.F Avatar answered Oct 04 '22 12:10

D.F


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

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.

like image 33
kovarex Avatar answered Oct 04 '22 10:10

kovarex