Hacker News new | ask | show | jobs
by rednum 2027 days ago
I vaguely remember learning similar fact years ago, but it was phrased something like "There are as many floats in [0, 1] range as in [1, infty]". Leaving aside where exactly "midpoint" of floats lies (either in Ruby's implementation or other languages'): what would be implications of this for writing code dealing with floats? Can I shoot myself in the foot somehow with low precision if numbers I'm using are "too close" to infinity?
4 comments

What you describe is a common problem for video games, because if the engine uses "absolute" floats for everything in the game you effectively lose precision as you move away from the origin. If your maps get very large it gets very noticeable. I remember for instance in No Man's Sky when you traveled very far in a system you'd start to see the various animations become very jerky because the precision would become too low to represent the coordinates of the intermediary steps correctly.

See a random video showcasing the issue here: https://youtu.be/D2XX2ZnRk8M?t=197

You can see all the moving elements jerking around as they "snap" to the next available float value.

A common fix for this issue is to use two sets of coordinates: you can for instance represent your world as a grid with fixed-size cells, then you translate all your models into the local cell before computing anything, this way you always have good enough precision since you effectively limit the amplitude of your floats. Of course I handwave many complications here, such as what happens when you're at the edge of a cell for instance, but it's workable.

This video goes into something vaguely related with how the coordinate system works in Mario 64: https://youtu.be/kpk2tdsPh0A

There's a lot of interesting problems that arise when making a 3d game vs a 2d one.

This channel is a fantastic resource for anyone interested in SM64 hacking, here is another of his videos that specifically deals with floating point rounding error glitches: https://www.youtube.com/watch?v=9hdFG2GcNuA
> A common fix for this issue is to use two sets of coordinates: you can for instance represent your world as a grid with fixed-size cells, then you translate all your models into the local cell before computing anything, this way you always have good enough precision since you effectively limit the amplitude of your floats.

Isn't this in practise creating a double-precision float by adding a second "significant figure" in a "base float" system?

You can think of it that way, sure, but since you want your computations to remain as fast as possible you don't want to use the full "double-precision" float everywhere, so that's where it gets tricky. You essentially want to translate everything to your local referential once and for all, then do everything with local coordinates if possible.

It's somewhat reminiscent of segmented memory in a way.

>> a grid with fixed-size cells

> creating a double precision float ... ?

This added top-level is uniformly distributed - so yes, it's a more precise float, but no, it's not a direct analogue.

Kind of but you get significantly better precision than if you just use a normal double float type because doubles only push the issues further out. Especially in games you're likely already breaking the world into cells for streaming chunks into/out of memory anyways.
Isn't this also just pushing the problem out? Once you get far enough out, you wouldn't be able to transition between cells because of the rounding error of the cell boundary being bigger than the cell size.

You can't represent infinite precision with finite bits so you must run into an issue eventually.

Of course but remember that it grows exponentially with the number of bits. Every bit you add one bit it doubles your usable size.

The visible universe (according to Wikipedia) is 8.8 * 10^26 meters or about 5.4 * 10^61 Plank lengths. That gives us a log2 of 205.1

In other words if my maths is correct if you want to represent the entire visible universe down to the scale of the Planck length you need 206bits of resolution, or 4 64 bit integers with a lot of room to spare.

And if you don't actually aim to simulate subatomic particles you can get down to millimeter resolution with "only" 100bits.

And that's not even taking into account that, our universe being so empty, you can probably fudge super large distances a bit (having a lower resolution at scales where the universe is mostly empty). Nobody is going to notice if Andromeda if a few parsecs closer than it should be.

Yes, you can. A good way to think about it is that a floating point number has a fixed amount of significant digits. A 32-bit float has between 6 to 9 significant digits[0]; a 64-bit float ("a double") can represent just double that - i.e. ~16 significant figures.

This means you can represent 12345678 and 0.12345678 as 32-bit floats just fine, but if you try to add the two, you'll just get 12345678. But there's also a more subtle case, in which you try to add 1234.5678 to 0.12345678, and lose half of the significant digits from the smaller number.

These types of errors are common in calculations dealing with large differences in orders of magnitude, and a part of the common wisdom to not use floats for anything involving money.

Another common occurrence of precision errors happens when modelling large 2D or 3D environments in video games. The simplest approach starts with a global coordinate system, in which calculations take place. Since the number of significant digits remains fixed, the further you are from origin, the less precision you have available for your unit of measure of position - meaning the same calculations happening far away from (0, 0, 0) will have more errors than those happening close.

A somewhat well-known example of that was[1] Kerbal Space Program - a game that models realistic spaceflight within a fictional solar system. As you ventured further out and visited the most distant celestial bodies, you'd notice your ships would spontaneously shake apart and/or explode, as if physics stopped working. Affectionately called a "Kraken attack", it was due to floating point precision errors - that far out, in the global coordinate system, the difference between two closest representable positions became comparable with the size of parts from which the ships were built. With that big of a delta, physical calculations would round some forces down to zero, and massively magnify others, as simulated objects could suddenly only move in increments comparable with their own size.

--

[0] - The limitation is in binary, which doesn't map cleanly to base 10.

[1] - I think they got around to mitigating this eventually. One way you can approach such mitigation is by dividing your space into cells (and possibly subcells), each with its own coordinate system, and do as many calculations as you can within these cells, so that everything is close to the origin of their local coordinate system. Then, for (hopefully few) remaining calculations that involve multiple distant cells, you can use larger-precision representations, which are much more computationally expensive.

> [1] - I think they got around to mitigating this eventually.

Yes, as far as I know nowadays KSP works by inversing the point of reference, that is, your spaceship does not move and sits at (0,0,0) at all times, it's the whole solar system that moves around you.

Of course, functionnally it does not make a difference, and it solves the problem ;)

EDIT: ah, interesting that you have another theory, now I wonder :)

I don't know how they solved it in KSP, I was just presenting a general approach for when you need to have things happen in multiple places around a large simulated universe.

I can imagine KSP going with what you described, because in that game, things you aren't actively controlling and that aren't in "physics range" (2.5 kilometers IIRC) are either treated as stationary or on-rails (i.e. they're in orbit, and their positions are computed with a closed-form equation); anything that's neither stationary nor in orbit will be deleted once it leaves the physics range. This way, there is no physics simulation to be done on things you don't control or aren't withing few kilometers of, so centering the coordinate system at your active vessel would be a very good approach.

> Can I shoot myself in the foot somehow with low precision if numbers I'm using are "too close" to infinity?

The "number of sig figs" precision will be the same for large values far away from zero, as for values close to zero. E.g. IEEE 64 bit reliably gives you about 15 decimal digits of precision. Any decimal number with 15 digits of precision that is in the range of the IEEE 64 bit double can be converted to that type, and then back to decimal, such that all those digits of precision are recovered.

You can shoot yourself in the foot if you rely on floating-point values being able to exactly represent integers, but the values go beyond the range where that is possible. Beyond a certain range, all consecutive integers are no longer representable; some integers get approximated by nearby integers.

I guess the fact would be something like: "There are as many floats in [0,1] as integers in [1, infty]".

Probably you will have problems mixing operations between numbers too close to infinity with numbers too close to zero. But if you are working with floats you are probably going to shoot yourself in the foot anyway unless you understand IEEE floating point arithmetic, precisions and so on, so better stick to Decimals and BigDecimals if you can.