Wat is de beste efficiënte manier om een getal naar boven af te ronden en vervolgens af te kappen (verwijder decimalen na het afronden)?
Als decimaal bijvoorbeeld hoger is dan 0,5 (dat wil zeggen 0,6, 0,7, enzovoort), wil ik naar boven afronden en vervolgens afkappen (geval 1). Anders wil ik afkappen (geval 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)
Antwoord 1, autoriteit 100%
Er is geen ingebouwde math.round() functie in Lua, maar je kunt het volgende doen:
print(math.floor(a+0.5))
.
Antwoord 2, autoriteit 47%
Een truc die handig is voor het afronden op andere decimale cijfers dan hele gehele getallen, is om de waarde door opgemaakte ASCII-tekst te leiden en de tekenreeks %f
te gebruiken om de gewenste afronding te specificeren. Bijvoorbeeld
mils = tonumber(string.format("%.3f", exact))
rondt de willekeurige waarde in exact
af op een veelvoud van 0,001.
Een soortgelijk resultaat kan worden verkregen met schalen voor en na het gebruik van math.floor()
of math.ceil()
, maar de details goed krijgen volgens uw verwachtingen rond de behandeling van randgevallen kunnen lastig zijn. Niet dat dit geen probleem is met string.format()
, maar er is veel werk gestoken in het produceren van “verwachte” resultaten.
Afronding naar een veelvoud van iets anders dan een macht van tien vereist nog steeds schaal en heeft nog steeds alle lastige randgevallen. Een benadering die eenvoudig uit te drukken is en stabiel gedrag vertoont, is door te schrijven
function round(exact, quantum)
local quant,frac = math.modf(exact/quantum)
return quantum * (quant + (frac > 0.5 and 1 or 0))
end
en tweak de exacte toestand op frac
(en mogelijk het teken van exact
) om de randvakken te krijgen die u wilde.
Antwoord 3, Autoriteit 45%
Om ondersteunen ook negatieve getallen, gebruik dan deze:
function round(x)
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5)
end
Antwoord 4, Autoriteit 21%
Als uw Lua maakt gebruik van dubbele precisie IEC-559 (ook bekend als IEEE-754) drijft, zoals de meeste te doen, en je nummers zijn relatief klein (de methode werkt gegarandeerd voor inputs tussen -2 51 sup>en 2 51 sup>), de volgende efficiënte code verricht afronding met behulp van de huidige afrondingsmodus van uw FPU, die meestal rond is naar het dichtstbijzijnde, banden tot zelfs:
local function round(num)
return num + (2^52 + 2^51) - (2^52 + 2^51)
end
(merk op dat de cijfers tussen haakjes worden berekend op de compilatietijd; ze hebben geen invloed op runtime).
Bijvoorbeeld, wanneer de FPU is ingesteld op rond naar de dichtstbijzijnde of zelfs, wordt deze eenheidstest afgedrukt “Alle tests doorgegeven”:
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, including Ola M's answer,
-- 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^51-0.5, 2^51)
test(2^51-0.75, 2^51-1)
test(2^51-1.25, 2^51-1)
test(2^51-1.5, 2^51-2)
print("All tests passed")
Hier is een ander (minder efficiënt, natuurlijk) algoritme dat dezelfde FPU-afronding uitvoert, maar werkt voor alle nummers:
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
Antwoord 5, Autoriteit 13%
Hier is een om naar een willekeurig aantal cijfers (0, indien niet gedefinieerd):
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
Antwoord 6, Autoriteit 11%
Voor slechte afronding (het einde afsnijden):
function round(number)
return number - (number % 1)
end
Nou, als je wilt, kun je dit uitbreiden voor een goede afronding.
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))
Verwachte resultaten:
3
, 3
, 42
, 5
, 5
Antwoord 7, autoriteit 8%
Ik vind het bovenstaande antwoord van RBerteig leuk: mils = tonumber(string.format("%.3f", exact))
.
Uitgebreid tot een functieaanroep en een precisiewaarde toegevoegd.
function round(number, precision)
local fmtStr = string.format('%%0.%sf',precision)
number = string.format(fmtStr,number)
return number
end
Antwoord 8, autoriteit 3%
Moet math.ceil(a-0.5)
zijn om halve gehele getallen correct te verwerken
Antwoord 9, autoriteit 3%
Hier is een flexibele functie om op verschillende plaatsen af te ronden. Ik heb het getest met negatieve getallen, grote getallen, kleine getallen en allerlei randgevallen, en het is nuttig en betrouwbaar:
function Round(num, dp)
--[[
round a number to so-many decimal of places, which can be negative,
e.g. -1 places rounds to 10's,
examples
173.2562 rounded to 0 dps is 173.0
173.2562 rounded to 2 dps is 173.26
173.2562 rounded to -1 dps is 170.0
]]--
local mult = 10^(dp or 0)
return math.floor(num * mult + 0.5)/mult
end
Antwoord 10
Voor het afronden op een bepaald aantal decimalen (dat ook negatief kan zijn), stel ik de volgende oplossing voor die is gecombineerd met de bevindingen die al als antwoorden zijn gepresenteerd, met name de inspirerende gegeven door Pedro Gimeno. Ik heb een paar hoekcases getest waarin ik geïnteresseerd ben, maar kan niet beweren dat dit deze functie 100% betrouwbaar maakt:
function round(number, decimals)
local scale = 10^decimals
local c = 2^52 + 2^51
return ((number * scale + c ) - c) / scale
end
Deze gevallen illustreren de eigenschap round-halfway-to-even (die op de meeste machines standaard zou moeten zijn):
assert(round(0.5, 0) == 0)
assert(round(-0.5, 0) == 0)
assert(round(1.5, 0) == 2)
assert(round(-1.5, 0) == -2)
assert(round(0.05, 1) == 0)
assert(round(-0.05, 1) == 0)
assert(round(0.15, 1) == 0.2)
assert(round(-0.15, 1) == -0.2)
Ik ben me ervan bewust dat mijn antwoord het derde geval van de eigenlijke vraag niet behandelt, maar in het voordeel van IEEE-754 compliant te zijn, is mijn aanpak logisch. Dus ik zou verwachten dat de resultaten afhankelijk zijn van de huidige afrondingsmodus die is ingesteld in de FPU met FE_TONEAREST
is de standaard. En daarom lijkt het zeer waarschijnlijk dat na het instellen van FE_TOWARDZERO
(maar je kunt dat in Lua doen) deze oplossing precies de resultaten oplevert waar in de vraag om werd gevraagd.