Issue with string.format and/or math.floor?

I have this function which converts a number into a timestamp.

local function formatTime(t)
	local minutes = math.floor(t / 60)
	local seconds = math.floor(t) - minutes*60
	local milliseconds = t - seconds - minutes*60
	
	print(milliseconds, math.floor(milliseconds * 1000))

	return ("%02d:%02d:%03d"):format(minutes, seconds, milliseconds * 1000)
end

However, I’ve noticed for certain numbers the timestamp is innaccurate.

print(formatTime(5.069)) -- 00:05:068

This may actually a bug with Lua, but I figured I’d post it here first in case I’m missing something obvious.

What I suspect is the root issue is that math.floor is run internally when string.format is used when using the d format specifier.

You’ll notice in the function I included the line:
print(milliseconds, math.floor(milliseconds * 1000))

With the argument i plugged in I get 0.069 and 68 respectively which is incorrect as math.floor(0.069 * 1000) == 69.

Any help or suggestions would be greatly appreciated.

3 Likes

Hello, this is a known bug recognized by many others, this is currently being fixed, i’m not sure if it is to be fixed early or soon since the bug report was 5 months ago.

1 Like

Can you link me to the bug report please?

1 Like

Hello,

Here is the original bug report (July, 2020)

1 Like

This does not seem to be the same bug. That is timestamps for the dev console which are generated by default when printing. These timestamps are generated by my own code.

1 Like

This is definitely a bug related somehow to floating point numbers.

As a workaround, you could always add 0.5 (as that will behave like math.round instead).

local milliseconds = 0.069
milliseconds += 0.5 / 1000

math.floor(miliseconds * 1000) --> 69
3 Likes
local function formatTime(t)
	local minutes = math.floor(t / 60)
	local seconds = math.floor(t) - minutes*60
	local milliseconds = t - seconds - minutes*60
	
	
	local magicNumber = milliseconds* 1000
	print('Is the magic number 69? ',magicNumber==69)
	print("69 floored is ",math.floor(69))
	print(magicNumber, math.floor(magicNumber))
	
	return ("%02i:%02i:%03i"):format(minutes, seconds, milliseconds * 1000)
end

warn(formatTime(5.069)) -- 00:05:068

--[[
  Is the magic number 69?  false
  69 floored is  69 
  69 68 
  00:05:068 
]]

I’m small brained, but that number doesn’t seem to be 69 but rather some sort of decimal of some sort

1 Like

An easier way to show this is a floating point error is with a quick string.format:

print(string.format("%.15f", magicNumber)) --> 68.999999999999943

3 Likes

This is not a bug but a common problem in computer science caused by limited precision of floating point representation.

1 Like
local function formatTime(t)
	local minutes = math.floor(t / 60)
	local seconds = t % 60
	local milliseconds = (seconds * 1000) % 1000
        seconds = math.floor(seconds)
	return string.format("%02i:%02i:%03i", minutes, seconds, milliseconds)
end

Is this what you were looking for?

print(formatTime(5.069))

gave me
00:05:069

This is rather due to the internal formatting of lua (and many other languages). Automatic rounding is performed to represent the numbers in the best possible way. Milliseconds is actually less than 0.069

print(("%.20f"):format(milliseconds)) --  0.06899999999999995026

so truncating the number with math.floor will give 0.068. If you want to be more precise you should apply rounding instead of truncation.