Lua: округление чисел, а затем усечение

Каков наилучший эффективный способ округлить число, а затем усечь его (удалить после запятой после запятой)?

например, если десятичная цифра выше 0,5 (то есть 0,6, 0,7 и т.д.), я хочу округлить, а затем усечь (случай 1). В противном случае я хотел бы усечь (случай 2)

for example:
232.98266601563 => after rounding and truncate = 233 (case 1)
232.49445450000 => after rounding and truncate = 232 (case 2)
232.50000000000 => after rounding and truncate = 232 (case 2)

Ответ 1

В Lua нет встроенной функции math.round(), но вы можете сделать следующее: print(math.floor(a+0.5)).

Ответ 2

Трюк, полезный для округления десятичных цифр, отличных от целых чисел, должен передать значение через форматированный текст ASCII и использовать строку формата %f для указания желаемого округления. Например

mils = tonumber(string.format("%.3f", exact))

будет округлять произвольное значение в exact до кратного 0.001.

Аналогичный результат может быть получен с масштабированием до и после использования одного из math.floor() или math.ceil(), но получение информации в соответствии с вашими ожиданиями, связанными с обработкой краевых случаев, может быть сложным. Не то, чтобы это не проблема с string.format(), но большая работа заставила его произвести ожидаемые результаты.

Округление до кратного чего-то, кроме десяти, по-прежнему потребует масштабирования и все еще имеет все сложные случаи. Один из подходов, который просто выразить и имеет устойчивое поведение, состоит в том, чтобы написать

function round(exact, quantum)
    local quant,frac = math.modf(exact/quantum)
    return quantum * (quant + (frac > 0.5 and 1 or 0))
end

и подстройте точное условие на frac (и, возможно, знак exact), чтобы получить нужные вам края.

Ответ 3

Чтобы также поддерживать отрицательные числа, используйте это:

function round(x)
  return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5)
end

Ответ 4

Должно быть math.ceil(a-0.5) правильно обрабатывать полуцелые числа

Ответ 5

Здесь одно для округления до произвольного числа цифр (0, если не определено):

function round(x, n)
    n = math.pow(10, n or 0)
    x = x * n
    if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end
    return x / n
end

Ответ 6

Для плохого округления (обрезка конца):

function round(number)
  return number - (number % 1)
end

Ну, если хочешь, можешь расширить это для хорошего округления.

function round(number)
  if (number - (number % 0.1)) - (number - (number % 1)) < 0.5 then
    number = number - (number % 1)
  else
    number = (number - (number % 1)) + 1
  end
 return number
end

print(round(3.1))
print(round(math.pi))
print(round(42))
print(round(4.5))
print(round(4.6))

Ожидаемые результаты:

3, 3, 42, 5, 5

Ответ 7

Мне нравится ответ выше RBerteig: mils = tonumber(string.format("%.3f", exact)). Расширил его до вызова функции и добавил значение точности.

function round(number, precision)
   local fmtStr = string.format('%%0.%sf',precision)
   number = string.format(fmtStr,number)
   return number
end

Ответ 8

Если ваш Lua использует поплавки IEC-559 (он же IEEE-754) двойной точности, как это делают большинство, и ваши числа относительно невелики (метод гарантированно работает для входов между -2 50 и 2 50), следующий эффективный код выполнит округление с использованием текущего режима округления FPU, который обычно округляется до ближайшего, связан с четным:

local function round(num)
  return num + (2^52 + 2^51) - (2^52 + 2^51)
end

Например, если для FPU установлено значение округления до ближайшего или даже четного, этот unit тест выводит "Все тесты пройдены":

local function testnum(num, expected)
  if round(num) ~= expected then
    error(("Failure rounding %.17g, expected %.17g, actual %.17g")
          :format(num+0, expected+0, round(num)+0))
  end
end

local function test(num, expected)
  testnum(num, expected)
  testnum(-num, -expected)
end

test(0, 0)
test(0.2, 0)
test(0.4, 0)
-- Most rounding algorithms you find on the net fail this one:
test(0.49999999999999994, 0)
-- Ties are rounded to the nearest even number, rather than always up:
test(0.5, 0)
test(0.5000000000000001, 1)
test(1.4999999999999998, 1)
test(1.5, 2)
test(2.5, 2)
test(3.5, 4)
test(2^50-0.5, 2^50)
test(2^50-1.5, 2^50-2)
print("All tests passed")

Вот еще один (конечно, менее эффективный) алгоритм, который выполняет такое же округление FPU, но работает для всех чисел:

local function round(num)
  local ofs = 2^52
  if math.abs(num) > ofs then
    return num
  end
  return num < 0 and num - ofs + ofs or num + ofs - ofs
end