How do I fill up a number's decimal places with zeroes? - lua

Assume the following numbers:
local a = 2
local b = 3.1
local c = 1.43
local d = 1.0582
My goal is to round these numbers to two decimal places. The result should be this, respectively:
a = 2.00
b = 3.10
c = 1.43
d = 1.06 or 1.05
Obviously I understand that any number with trailing decimal zeroes will get rounded. 2.00 will be 2. But I need the numbers as strings, and to make it visually more appealing, I would need these two decimal places.
Here's a function I use to round to two decimal places:
function round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end
This works fine for test cases c and d, but will produce wrong results with a and b: it won't fill up with zeroes. I understand it is because the rounding function takes the numbers and calculates them - therefore the excess zeroes get cut off.
But that is exactly not my goal - not cutting them off.
I've tried string manipulation, by checking if and where a . is in a number, but that didn't work at all, for any case. My method:
local zei
if i < 100 then
if tostring(i):find("%.") == nil then
zei = round(i, 2) .. ".00" --No decimal point found, append .00
else
zei = round(i, 2) --Found decimal point, round to 2
end
if tostring(i):find("%.")+2 == tostring(i):len() then
zei = round(i, 2) .. "0" --Found point, but only one trailing number, append 0
end
else
zei = round(i, 0) --Number is over 100, no decimal points needed
end
The above 100 case is just for aesthetics and not relevant here. Where zei is the displayed string, and i is one of the test case numbers.
Summary
How would I round a number to two decimal places, but append trailing zeroes, even if they were excess, e.g. 2.30? I understand I need strings for this.
Contradicting question: Strip off excess zeroes

You don't round numbers. You create string representations of those numbers. That would be done by string.format, with an appropriate format. Like this:
string.format("%.2f", a);

Related

Reliable way of getting the exact decimals from any number

I'm having problem returning spesific amount of decimal numbers from this function, i would like it to get that info from "dec" argument, but i'm stuck with this right now.
Edit: Made it work with the edited version bellow but isn't there a better way?
local function remove_decimal(t, dec)
if type(dec) == "number" then
for key, num in pairs(type(t) == "table" and t or {}) do
if type(num) == "number" then
local num_to_string = tostring(num)
local mod, d = math.modf(num)
-- find only decimal numbers
local num_dec = num_to_string:sub(#tostring(mod) + (mod == 0 and num < 0 and 3 or 2))
if dec <= #num_dec then
-- return amount of deciamls in the num by dec
local r = d < 0 and "-0." or "0."
local r2 = r .. num_dec:sub(1, dec)
t[key] = mod + tonumber(r2)
end
end
end
end
return t
end
By passing the function bellow i want a result like this:
result[1] > 0.12
result[2] > -0.12
result[3] > 123.45
result[4] > -1.23
local result = remove_decimal({0.123, -0.123, 123.456, -1.234}, 2)
print(result[1])
print(result[2])
print(result[3])
print(result[4])
I tried this but it seems to only work with one integer numbers and if number is 12.34 instead of 1.34 e.g, the decimal place will be removed and become 12.3. Using other methods
local d = dec + (num < 0 and 2 or 1)
local r = tonumber(num_to_string:sub(1, -#num_to_string - d)) or 0
A good approach is to find the position of the decimal point (the dot, .) and then extract a substring starting from the first character to the dot's position plus how many digits you want:
local function truncate(number, dec)
local strnum = tostring(number)
local i, j = string.find(strnum, '%.')
if not i then
return number
end
local strtrn = string.sub(strnum, 1, i+dec)
return tonumber(strtrn)
end
Call it like this:
print(truncate(123.456, 2))
print(truncate(1234567, 2))
123.45
1234567
To bulk-truncate a set of numbers:
local function truncate_all(t, dec)
for key, value in pairs(t) do
t[key] = truncate(t[key], dec)
end
return t
end
Usage:
local result = truncate_all({0.123, -0.123, 123.456, -1.234}, 2)
for key, value in pairs(result) do
print(key, value)
end
1 0.12
2 -0.12
3 123.45
4 -1.23
One could use the function string.format which is similar to the printf functions from C language. If one use the format "%.2f" the resulting string will contain 2 decimals, if one use "%.3f" the resulting string will be contain 3 decimals, etc. The idea is to dynamically create the format "%.XXXf" corresponding to the number of decimal needed by the function. Then call the function string.format with the newly created format string to generate the string "123.XXX". The last step would be to convert back the string to a number with the function tonumber.
Note that if one want the special character % to be preserved when string.format is called, you need to write %%.
function KeepDecimals (Number, DecimalCount)
local FloatFormat = string.format("%%.%df", DecimalCount)
local String = string.format(FloatFormat, Number)
return tonumber(String)
end
The behavior seems close to what the OP is looking for:
for Count = 1, 5 do
print(KeepDecimals(1.123456789, Count))
end
This code should print the following:
1.1
1.12
1.123
1.1235
1.12346
Regarding the initial code, it's quite straight-forward to integrate the provided solution. Note that I renamed the function to keep_decimal because in my understanding, the function will keep the requested number of decimals, and discard the rest.
function keep_decimal (Table, Count)
local NewTable = {}
local NewIndex = 1
for Index = 1, #Table do
NewTable[NewIndex] = KeepDecimal(Table[Index], Count)
NewIndex = NewIndex + 1
end
return NewTable
end
Obviously, the code could be tested easily, simply by copy and pasting into a Lua interpreter.
Result = keep_decimal({0.123, -0.123, 123.456, -1.234}, 2)
for Index = 1, #Result do
print(Result[Index])
end
This should print the following:
0.12
-0.12
123.46
-1.23
Edit due to the clarification of the need of truncate:
function Truncate (Number, Digits)
local Divider = Digits * 10
local TruncatedValue = math.floor(Number * Divider) / Divider
return TruncatedValue
end
On my computer, the code is working as expected:
> Truncate(123.456, 2)
123.45

How do I round a number in lua to a specific decimal digit?

I need a way to round my decimal in lua.
Sometimes my number looks like this:
When I search for it online, I only find solutions to round it to a whole number, but I don't want to round my variable to 0.00, 1.00, or 2.00, how would I round it to a specific decimal digit?
Code:
health = 1
maxhp = 2
function hp_showcase()
makeLuaText("hpcounter", "HP: "..health.."/"..maxhp.."", 2250, 30, 350)
addLuaText("hpcounter")
end
function opponentNoteHit(id, noteData, noteType, isSustainNote)
hp_showcase();
end
You can define a function that takes in the value to be rounded and the digit position you would like to round to, for this example positions in front of the . are positive and behind are negative so 2 rounds to the nearest 100 and -2 rounds to the nearest 0.01
local value = 0.79200750000001
local function round(number, digit_position)
local precision = math.pow(10, digit_position)
number = number + (precision / 2); -- this causes value #.5 and up to round up
-- and #.4 and lower to round down.
return math.floor(number / precision) * precision
end
print(value)
print(round(value, -2))
print(round(value, -1))
print(round(value, 0))
Results:
0.79200750000001
0.79
0.8
1

Lua: Type of a character

I need a function
function getCharType(c)
local i = string.byte(c) -- works only for 1 byte chars
if (i > 48) and (i < 57) then return 1 end
if (i > 97) and (i < 122) then return 2 end
return 0
end
which should return
2 - if c is a letter
1 - if c is a digit
0 - if c is a symbol (anything else)
c itself will already be a lower case character: charType = getCharType(string.lower(Character)). If Unicode characters are possible, that would be fine.
With the above getCharType("ö") is 0.
To find out whether a non-ASCII character is an uppercase or lowercase letter or a number, you need Unicode data. Module:Unicode data on Wikipedia has a function like this that uses Module:Unicode data/category (data for the General Category of Unicode characters).
Here's an adaptation of the lookup_category function from Module:Unicode data. I haven't included the Unicode data (Module:Unicode data/category); you will have to copy it from the link above.
local category_data -- set this variable to the table in Module:Unicode data/category above
local floor = math.floor
local function binary_range_search(code_point, ranges)
local low, mid, high
low, high = 1, #ranges
while low <= high do
mid = floor((low + high) / 2)
local range = ranges[mid]
if code_point < range[1] then
high = mid - 1
elseif code_point <= range[2] then
return range
else
low = mid + 1
end
end
return nil
end
function get_category(code_point)
if category_data.singles[code_point] then
return category_data.singles[code_point]
else
local range = binary_range_search(code_point, category_data.ranges)
return range and range[3] or "Cn"
end
end
The function get_category takes a code point (a number) and returns the name of the General Category. I guess the categories you are interested in are Nd (number, decimal digit) and the categories that begin with L (letter).
You will need a function that converts a character to a codepoint. If the file is encoded in UTF-8 and you are using Lua 5.3, you can use the utf8.codepoint function: get_category(utf8.codepoint('ö')) will result in 'Ll'. You can convert category codes to the number value that your function above uses: function category_to_number(category) if category == "Nd" then return 1 elseif category:sub(1, 1) == "L" then return 2 else return 0 end end.
Works only with ASCII characters (not Unicode)
function getCharType(c)
return #c:rep(3):match(".%w?%a?")-1
end

How to get each individual digit of a given number in Basic?

I have one program downloaded from internet and need to get each digit printed out from a three digit number. For example:
Input: 123
Expected Output:
1
2
3
I have 598
Need to Get:
5
9
8
I try using this formula but the problem is when number is with decimal function failed:
FIRST_DIGIT = (number mod 1000) / 100
SECOND_DIGIT = (number mod 100) / 10
THIRD_DIGIT = (number mod 10)
Where number is the above example so here is calulation:
FIRST_DIGIT = (598 mod 1000) / 100 = 5,98 <== FAILED...i need to get 5 but my program shows 0 because i have decimal point
SECOND_DIGIT = (598 mod 100) / 10 = 9,8 <== FAILED...i need to get 9 but my program shows 0 because i have decimal point
THIRD_DIGIT = (598 mod 10) = 8 <== CORRECT...i get from program output number 8 and this digit is correct.
So my question is is there sample or more efficient code that get each digit from number without decimal point? I don't want to use round to round nearest number because sometime it fill failed if number is larger that .5.
Thanks
The simplest solution is to use integer division (\) instead of floating point division (/).
If you replace each one of your examples with the backslash (\) instead of forward slash (/) they will return integer values.
FIRST_DIGIT = (598 mod 1000) \ 100 = 5
SECOND_DIGIT = (598 mod 100) \ 10 = 9
THIRD_DIGIT = (598 mod 10) = 8
You don't have to do any fancy integer calculations as long as you pull it apart from a string:
INPUT X
X$ = STR$(X)
FOR Z = 1 TO LEN(X$)
PRINT MID$(X$, Z, 1)
NEXT
Then, for example, you could act upon each string element:
INPUT X
X$ = STR$(X)
FOR Z = 1 TO LEN(X$)
Q = VAL(MID$(X$, Z, 1))
N = N + 1
PRINT "Digit"; N; " equals"; Q
NEXT
Additionally, you could tear apart the string character by character:
INPUT X
X$ = STR$(X)
FOR Z = 1 TO LEN(X$)
SELECT CASE MID$(X$, Z, 1)
CASE " ", ".", "+", "-", "E", "D"
' special char
CASE ELSE
Q = VAL(MID$(X$, Z, 1))
N = N + 1
PRINT "Digit"; N; " equals"; Q
END SELECT
NEXT
I'm no expert in Basic but looks like you have to convert floating point number to Integer. A quick google search told me that you have to use Int(floating_point_number) to convert float to integer.
So
Int((number mod 100)/ 10)
should probably the one you are looking for.
And, finally, all string elements could be parsed:
INPUT X
X$ = STR$(X)
PRINT X$
FOR Z = 1 TO LEN(X$)
SELECT CASE MID$(X$, Z, 1)
CASE " "
' nul
CASE "E", "D"
Exponent = -1
CASE "."
Decimal = -1
CASE "+"
UnaryPlus = -1
CASE "-"
UnaryNegative = -1
CASE ELSE
Q = VAL(MID$(X$, Z, 1))
N = N + 1
PRINT "Digit"; N; " equals"; Q
END SELECT
NEXT
IF Exponent THEN PRINT "There was an exponent."
IF Decimal THEN PRINT "There was a decimal."
IF UnaryPlus THEN PRINT "There was a plus sign."
IF UnaryNegative THEN PRINT "There was a negative sign."

How to generate random lines of text of a given length from a dictionary of words (bin-packing problem)?

I need to generate three lines of text (essentially jibberish) that are each 60 characters long, including a hard return at the end of each line. The lines are generated from a dictionary of words of various lengths (typically 1-8 characters). No word may be used more than once, and words must be separated by spaces. I think this is essentially a bin-packing problem.
The approach I've taken so far is to create a hashMap of the words, grouped by their lengths. I then choose a random length, pull a word of that length from the map, and append it to the end of the line I'm currently generating, accounting for spaces or a hard return. It works about half the time, but the other half of the time I'm getting stuck in an infinite loop and my program crashes.
One problem I'm running into is this: as I add random words to the lines, groups of words of a given length may become depleted. This is because there are not necessarily the same number of words of each length in the dictionary, e.g., there may only be one word with a length of 1. So, I might need a word of a given length, but there are no longer any words of that length available.
Below is a summary of what I have so far. I'm working in ActionScript, but would appreciate insight into this problem in any language. Many thanks in advance.
dictionary // map of words with word lengths as keys and arrays of corresponding words as values
lengths // array of word lengths, sorted numerically
min = lengths[0] // minimum word length
max = lengths[lengths.length - 1] // maximum word length
line = ""
while ( line.length < 60 ) {
len = lengths[round( rand() * ( lengths.length - 1 ) )]
if ( dictionary[len] != null && dictionary[len].length > 0 ) {
diff = 60 - line.length // number of characters needed to complete the line
if ( line.length + len + 1 == 60 ) {
// this word will complete the line exactly
line += dictionary[len].splice(0, 1) + "\n"
}
else if ( min + max + 2 >= diff ) {
// find the two word lengths that will complete the line
// ==> this is where I'm having trouble
}
else if ( line.length + len + 1 < 60 - max ) {
// this word will fit safely, so just add it
line += dictionary[len].splice(0, 1) + " "
}
if ( dictionary[len].length == 0 ) {
// delete any empty arrays and update min and max lengths accordingly
dictionary[len] = null
delete dictionary[len]
i = lengths.indexOf( len )
if ( i >= 0 ) {
// words of this length have been depleted, so
// update lengths array to ensure that next random
// length is valid
lengths.splice( i, 1 )
}
if ( lengths.indexOf( min ) == -1 ) {
// update the min
min = lengths[0]
}
if ( lengths.indexOf( max ) == -1 ) {
// update the max
max = lengths[lengths.length - 1]
}
}
}
}
You should think of an n-letter word as being n+1 letters, because each word has either a space or return after it.
Since all your words are at least 2 characters long, you don't ever want to get to a point where you have 59 characters filled in. If you get to 57, you need to pick something that is 2 letters plus the return. If you get to 58, you need a 1-letter word plus the return.
Are you trying to optimize for time? Can you have the same word multiple times? Multiple times in one line? Does it matter if your words are not uniformly distributed, e.g. a lot of lines contain "a" or "I" because those are the only one-letter words in English?
Here's the basic idea. For each line, start choosing word lengths, and keep track of the word lengths and total character count so far. As you get toward the end of the line, choose word lengths less than the number of characters you have left. (e.g. if you have 5 characters left, choose words in the range of 2-5 characters, counting the space.) If you get to 57 characters, pick a 3-letter word (counting return). If you get to 58 characters, pick a 2-letter word (counting return.)
If you want, you can shuffle the word lengths at this point, so all your lines don't end with short words. Then for each word length, pick a word of that length and plug it in.
dictionnary = Group your words by lengths (like you already do)
total_length = 0
phrase = ""
while (total_length < 60){
random_length = generate_random_number(1,8)
if (total_length + random_length > 60)
{
random_length = 60 - total_length // possibly - 1 if you cound \n and -2 if you
// append a blank anyway at the end
}
phrase += dictionnary.get_random_word_of_length(random_length) + " "
total_length += random_length + 1
}

Resources