Кросс-платформенный / компилятор согласованный 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/)

4 ответов


вы можете перейти к LuaJIT. Он форматирует номера последовательно между платформами.

с расширения страницы:

tostring () etc. канонизировать NaN и ±Inf

все преобразования числа в строку последовательно преобразуют небесконечные числа в одни и те же строки на всех платформах. NaN приводит к "nan", положительная бесконечность приводит к" inf "и отрицательная бесконечность приводит к"- inf".

tonumber() так далее. используйте встроенную строку для преобразования чисел

все преобразования строки в число последовательно преобразуют целочисленные и входы с плавающей запятой в десятичные и шестнадцатеричные на всех платформах. strtod() не используется, что позволяет избежать многочисленных проблем с плохими реализациями C-библиотеки. Встроенная функция преобразования обеспечивает полную точность в соответствии со стандартом IEEE-754, работает независимо от текущей локали и поддерживает шестнадцатеричные числа с плавающей запятой (например 0x1.5p-3).


самый проголосовавший ответ неверен, потому что документация неверна в первую очередь.

Это то, что происходит в LuaJIT:

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

смотрим tonumber и tostring реализация это макросы, вызываемые для получения результатов.

не стесняйтесь исправлять этот ответ, если вы найдете реальную" встроенную " реализацию, потому что мне любопытно, как это действительно работает.


год спустя, вот как мы решили это.

мы загрузили пользовательскую реализацию печати (trio) и принудительное использование этой реализации вместо системного в lua (и наших источниках).

мы также должны были изменить

long double trio_long_double_t;

to

double trio_long_double_t;

в triodef.h для обеспечения того, чтобы Visual studio и linux / mac давали одинаковые результаты.


Lua дает вам математику.frexp в стандартной библиотеке. Вы можете использовать это, чтобы разделить поплавки на экспоненту и форму мантиссы, а затем выполнить пользовательскую печать в чистом Lua, которая не будет зависеть от базовой платформы. Вот пример:

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

PS наслаждаясь пятничными фактами, продолжайте их приходить:)