Hello. I recently discovered floating point errors and I thought about something. There's a lot of complicated code that I don't really feel the need to explain, but basically, I have a combat system in which you slowly charge up a meter, and many upgrades that slightly affect the speed and values of that meter. I've been using floating point numbers that range from 0-100 with precision to the hundreds place, but I was wondering if it would actually be a smarter option to use an integer that can range from 0 to 10,000 for these calculations. I think there have been a couple times that I've lost a bit of precision due to floating point errors, and I wanted to know if this would be a workaround, or if this would introduce new problems. Thanks in advance!
Yes, that is better if you need that precision. I believe almost all banking systems use cents as their “ints”. So one dollar would be 100 cents, not a cent 1/100 of a dollar. So you would try to avoid floating point errors in the same way banks do it.
You can’t avoid floating point errors. They are a fact of life. If you think about it - integers will also have the precision limit, as they’re limited to whole numbers.
Show a specific example of a problem if you want some suggestions on how to work around it.
never check equality; if you want an equality check, use is_equal_approx() or is_zero_approx()– direct equality checks are likely to fail due to tiny rounding differences
if you can, use greater/less than checks instead
floating point as implemented on modern CPUs can’t perfectly represent many values; classically in float, (1 / 3) * 3 == 1is false (though see this paper by Hehner and Horspool for an alternate world where some of this is better handled…)
most of the precision in float is around zero; internally (handwaving a bit) it’s an integer value being raised to an integer power – 1.3 * 1.3 will give you closer to the real math result than 10000000.3 * 100000000.3
the power part also means that precision drops off fast at the bottom; famously, for example, if you loop on a float value adding 1 to it, eventually it stops increasing because the 1 you’re adding is too small to be represented at the power it’s being raised to
this also means that precision loss is partly a function of the difference in magnitude between the values – 10000.0 * 0.0003 has 8 orders of magnitude difference, for example
chaos (in the math sense) is also a factor here; the more operations you perform (particularly things like multiplication), the more roundoff error compounds – try to recompute things from error-free start points once in a while, if you can
You are probably fine, keep in mind Godot uses double-precision floats for variables with type float and single-precision for Vector/Matrix types. There is very likely enough precision in the 0-100 range for your applications, if the main concern is timers (and thus adding up rounding errors) then you could use time stamps with Time.get_ticks_msec() to avoid continuously adding, still there is very likely enough precision for your purposes.
# Example of adding up rounding errors; again time_left will use double-precision
# so it will likely play out just fine on any machine with any frame rate, even very fast machines
var time_left: float = 1.0
func _process(delta: float) -> void:
time_left -= delta
if time_left < 0:
print("One second has passed!")
# The same example using timestamps to avoid cumulative operations
# This happens to use integers, but a float timestamp avoids the same cumulative rounding errors
@onready var time_stamp: int = Time.get_ticks_msec()
func _process(_delta: float) -> void:
var this_time: int = Time.get_ticks_msec()
if this_time > time_stamp + 1000:
print("One second has passed!")
Infinite game worlds are generally the only case where floating point precision errors will appear and you must be careful, in part because Vectors and the GPU will use single-precision.