Search Unity

  1. The 2022.1 beta is now available for testing. To find out what's new, have a look at our 2022.1 beta blog post.
    Dismiss Notice

Floating point accuracy

Discussion in 'Scripting' started by AnimalMan, Jan 15, 2022 at 3:38 AM.

  1. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    317
    So we all know that the infinite environment fails when vector 3 position is greater than the floating point accuracy of the object you are translating. And this is fine.

    but I wanted to get an opinion on countering this issue to grant a truly non repeating near infinite environment up to Math infinity - and so I thought okay so -

    what is translation? - the act of moving the object by some decimal place of a float. And so if you were zoomed in far enough anyway the teleporting translation would be visible and the object would seem glitchy, until you zoomed out.


    So



    what’s the viability of zooming out and translating by int ? So the minimal move distance is 1 in integer.
    Will this still cause issues when trying to have an absolutely huge non repeating environment? Could I / in theory —/ comfortably exceed the normal bounds of the playable region by never using floats and simply rescaling 0.0001 via camera so that it’s 1 int each time.


    What I mean is —— if my cameras distance from the object is so great that 1 int of movement looks just as accurate as 0.0001 float of movement, do I have access to eternally fly a space ship around the infinite environment without issue?


    Thoughts?
     
    Last edited: Jan 15, 2022 at 3:43 AM
  2. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    17,372
    I have to admit your post left me confused as to what you are asking. If your goal is to still use floating point numbers but simply shift where the number is in relation to the decimal point that won't solve anything because that's not how floating point numbers work.

    A very simple explanation is that a floating point number consists of a sign bit representing positive or negative, an offset value that says how far from the decimal point the number is, and an integer to represent the number. For a float that integer is capped at seven digits of accuracy. For doubles it is fifteen digits.

    Both 9,999,999 and 9,999.999 are valid single precision numbers. One has a greater distance (~10,000,000 meters versus ~10,000 meters) it can represent but the other has a greater precision (thousandths of a meter versus one meter).

    If you want a truly near infinite world the only way to do that is to completely abandon storing the world directly in Unity as game objects instead storing it in your own data layout using data types capable of higher accuracy. When you want to render part of the world you simply translate that part into game object form.
     
    Last edited: Jan 15, 2022 at 10:16 AM
  3. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,007
    The approach you described, of using integers multiplied by some constant, is called "fixed point", if you want to read more about it.

    It gives you constant precision regardless of distance, but it's not "infinite" (there's no such thing as infinite anything with computers). At the end of the day a 32-bit number (int or float) can only represent 4,294,967,296 unique values. Using fixed point or floating point only changes how those values are distributed (linear versus logarithmic).

    Instead of dividing by 100 or 1000, a more performant approach is to do it using bitwise shift operations, which are cheaper than multiplication and division. This allows you to choose how many bits you're going to use for the whole part and how many for the decimal part.

    For example, if you use 22 bits for the whole and 10 bits for the decimal part, you can have numbers ranging from -2,097,152 to 2,097,151 with a precision of 1/1024 (0.0009765625).

    https://en.m.wikipedia.org/wiki/Fixed-point_arithmetic
     
  4. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    317
    Thank you very much for detailed responses and education here.

    you are ultimately saying unity will use floating point and result an accuracy anomaly such as 1.00001 when position is set by int. as it would still use the floating points since it’s an engine built around it as Neto saying here, the scale of environment currently useable is the limit of floating point values as available in unity.

    Makes sense, it will save me lots of meaningless test work thank you.
     
  5. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    28,328
    Some problems:
    • URP doesn't have camera relative rendering yet (but it is coming), HDRP does
    • even if you control your fixed point, the rest of Unity doesn't such as animation, particles, physics, etc
    So I would just stick to origin shifting (act of translating the world every so often to center around zero again)
     
    Bunny83 and AnimalMan like this.
  6. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    317
    Also I noticed that a 360 rotation using an integer system could only ever have 360 turns making rotation really jittery I imagine. Unless the degrees of a circle were scaled up to account for it being an integer. The benefits I can see for running an integer through this system or making an integer based physics system - would be that you never actually have to round any of your numbers for any reason. Such as an expected position of 1,1,1 if asking is .x = 1 sometimes you can’t get a true back from it because of the float - without having to round that value first or cast it as integer. I am also not sure what value exponents start to enter the fray but am sure the gains of an integer system will wind up with the same limitations regardless of additional space.
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    21,292
    Using an integer sure, but using an integer and treating it as fixed point, it can have as much precision as you like, within the limits of the bits of your fixed point. Before feeding it into the Quaternion.Euler() factory you would create a float out of it.

    BUT... since rotations are really only useful for 0 to 360, why not periodically normalize it back to that range so you never have to worry about floating point precision?
     
  8. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    317
    The alternative solution is to move the entire environment around the player I guess. I think both methods show the same flaw in that I wanted my object to be say (idk) 10^6 from vector 3 zero. Somewhere really far away from vector zero and not have to worry about rendering, position or rotation glitches on the assumption of having a system that takes only whole values in the form of an int that will never gain or lose any additional accuracy .00001 will only ever be read as an integer. But I do believe the theory is fundamentally flawed in that the act of scaling the camera to fool the system and acquire more useable digits of accuracy (should an integer system exist) will actually wind up with practically the same size of environment as provided by floating point though technically the environment is larger as the entire system has to be scaled the act of scaling movements for example shrinks everything that an integer system would gain to likely something of similar size to a floating point universes.
    I understand using a custom number or long system where we are creating our own number of a set length is the way to get hold of more playable space, but I don’t yet have the knowledge to make an engine from scratch and the one project I would use it for is probably not worth it. Plus other anomaly may come into play. Such as ;; dealing with excruciatingly large numbers could result in much slower mathematics calculations.
     
  9. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    7,859
    It is definitely flawed since no matter the scale, float's have the same amount of precision. 24-binary bits (~7.225 decimal digits), that's it, per the IEEE-754 specification:
    https://en.wikipedia.org/wiki/Single-precision_floating-point_format


    (note, there is 23 stored, plus 1 implicit bit since all non-zero floats have a leading 1. You don't actually have to store it.)

    An int technically would give you more precision, since it was result in 32-bits of precision. But it would then be fixed point which means you'd have a static scale range.

    And as has been mentioned, since unity fundamentally relies on floats... no matter what you'd end up back in float space in the end and everything that comes with it.

    ...

    Anyways... IF we wanted to do this, and IF unity's fundamental usage of 'float' wasn't a concern. I wouldn't use int (fixed point, or whole value).

    I'd use double.

    Get a whopping 53-bits of precision (~15.995 decimal digits):
    https://en.wikipedia.org/wiki/Double-precision_floating-point_format


    That whole "floating" part of floats is the nice thing to keep. Allows for the whole scaling precision business.

    ...

    I'll say this though. In all my years of using Unity. This very same topic pops into my head while I'm laying in bed at night. And I dream about potential work arounds wanting to figure out something that smoothly integrates with Unity.

    But I never come up with one that is smooth. Just iterations on the existing such as the moving play field.

    And the thing... honestly... I've yet to make a game where it actually impacts me to a point that the existing work arounds aren't easy enough as is.
     
  10. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    317
    This is true
    1. players will not notice or appreciate the work
    2. The potential code savings are not meaningful
    3. Game design needs to actually use the space or else it’s just totally not worth it.

    The classic teleporting technique is what I’ll use for my project though I might do something like force a direction change instead so player can cruise along the boundary knowing it’s the boundary.



    I wonder if this is where the concept of a multiverse originated.
     
  11. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    17,372
    What code savings?
     
  12. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    317
    No Rounding errors in detection of values


    X pos = 12647483
    Does X = 12647483 ?
    Result no
    Why? Because X = 12647483.00001

    I would consider this to be a saving as you just read the value instead of reformatting the value for your result. So if you instance a grid say a hex grid idk 150x150 and you spawned them all +1.5 from previous position for alignment purposes. You won’t be able to get those values you set at their accuracy without having to round each request.

    so in a game where you just want to use an objects position to find out which cell it’s standing on, the two values are essentially the same, but they cannot be obtained without rounding. So in the case where you are actually using a float and your position is 1.5 but your cell is at 1.500746 you have to go ahead and write code to remove those places.
     
    Last edited: Jan 16, 2022 at 12:19 PM
  13. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    1,245
    Since this is about float precision I'll just drop my float precision table here as a start. I haven't made one for double yet but double does solve a lot if issues for quite a huge range already. Minecraft uses doubles for almost everything. Though the world is technically not infinite. Well true infinity can't really be achieved since all number formats would need to be able to grow in memory infinitely. Though what we mean when we talk about infinite worlds is: too large to care about.

    For really large worlds (like modelling space) you would always go for a floating origin approach. Every "standalone" object or root object in the world would be positioned with an integer "sector" index and a float offset inside the sector / chunk / region. With floats it's best to not go larger than 1000. So you may construct regions which are about 1000 in size. When using a long to specify the region index you have an incredible large world with a consistent precision. Everything that is happening within a region is done in relation to the region root object. So all is just relative positions within that object. If you need world scale references, you would combine the region index with the offset position. Though in most cases such interactions would be rare. It matters as region borders. So when objects close to the border needs interactions, you have to take into account their relative offset due to the region index. Though everything is just relative. So if you are in region (0, 0, 532484) and another object is in (0, 0, 532485), all you need to know is that they are 1 region apart. Since each region has a floating origin they are positioned at multiples of 1000 in relation to the actual origin. So your current active region may be 532484 which sits at (0,0,0) and the other region (532485) is located relative to it at (0,0,1000). Anything that happens within a region is done with relative local positions inside a region.

    For space games most structures would not be visible when they are further away than a couple of "regions". Though large scale objects (planets, stars, ...) which may be thousands or million of regions away need to be rendered as well. You can not set your camera far clipping plane to 5 billion lightyears. However there are tricks how to render planets within a relatively small render distance. It's the old tricks we know from 2d games: fake 3d with parallax shift ^^. That simply means instead of rendering a planet at 5000000km away, we just render them close to the render distance. Of course since it's now much closer it needs to appear smaller by the same factor. So all we do is project the actual "universe" position onto the far clipping plane and scale the object down accordingly. Of course it's important to keep the distance ordering correct. So a common solution is to use a logarithmic mapping of infinite space into the actual depth buffer range. Here's an article about how the depth buffer can be used in a more effective way.

    Note that while the theory behind all of this isn't that complicated, actually implementing it an an efficient way is another story. Creating a custom infinite space renderer would be quite a bit of work. I'm sure there are already systems out there, though I guess most of them are not free of charge ^^. A quick look at the asset store brought up a Floating origin solution and a more advanced world streaming solution. Note those are just two examples I've found. I never used them so I can't say anything about if and how they work.
     
    koirat and AnimalMan like this.
  14. AnimalMan

    AnimalMan

    Joined:
    Apr 1, 2018
    Posts:
    317
    Thanks again for the help that binary float precision table is crazy. Unfortunately I probably won’t be pursuing this goal any more, but grateful for the education and time you guys all put in to the responses.



    the primary reason being that the benefits of having this are superficial and everything stacks quite abit, like I’ll need to save a lot of this data on top and reload it, assuming I can get a system to work, and provide additional space, and in that I can fill that space with interesting things, for example if the universe was somewhat procedural in its regions. So I feel like it’s not going to pay off for the trouble put into it, and the value to the core game experience (which is combat based) is pretty low considering we can use cheap methods to simply fool the environments scale. And atm the environment is not containing anything like asteroid fields, and I am generating regions in the form of a string, which are superficial tags that are always the same as they are based on a distance from vector 0, The downside being that if I implement a loop or teleport the entire universe cannot all be revisited, without installation of complex system to remember where I am. So I think it boils down to abit of weighing up and testing more elegant alternatives.
     
    Last edited: Jan 17, 2022 at 1:46 AM
unityunity