Подтвердить что ты не робот

Кросс-платформа/компилятор совместим с символом числа с плавающей запятой

У нас есть игра, которая должна быть детерминированной, поскольку она является частью его многопользовательской модели. Мы также используем 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/)
4b9b3361

Ответ 1

Через год мы так решили.

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

Нам также пришлось изменить

long double trio_long_double_t;

к

double trio_long_double_t;

в triodef.h, чтобы гарантировать, что Visual Studio и linux/mac дают те же результаты.

Ответ 2

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

На странице расширений:

tostring() и т.д. canonicalize NaN и ± Inf

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

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

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

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