0.05f - 0.05f = 3.72529E-09; I need some help.

I was trying to debug and figure out why I was unable to buy things in my game when the two values matched perfectly. I even wrote them out in 7 digit long floats and they looked perfect (0.0500000). But if I debug them subtracting one another I get the title of this thread. I’ve had this problem hundreds of times in my past with other languages, are there are any good ways to go about fixing this in C# or Unity special ways of dealing with it? I’m able to fix it buy making a function that does this:

    public bool CanBuyFactory(int index)
    {
        return Cash - GetFactoryCost(index) >= -0.009 ? true : false;
    }

But I’d like to know if there is a better way to go about this because this seems a silly function to have to call for every time I buy something, I’d much rather the math just work.

the first step is to change your “private void CanBuyFactory” to “private bool CanBuyFactory”.

But, to help skip this method:

what is “Cash”? and what does the GetFactoryCost(int) method do/return?

In the case of buying things, the answer is simple: never use a float to store currency. If your money system has dollars and cents, store it as an ‘int’ representing the number of cents. If you need subdivisions below cent, then decide ahead of time how deep you want to allow those subdivisions, and stick with it. Floating point inaccuracy is a fact of life, and if that’s a problem in a particular situation, you should reconsider whether a float is the right data type for that situation.

I’ve thought about this before but always imagined it would build up a lot of memory usage.

You could also use ‘decimal’ data type which is designed to be accurate in base 10:

Only downside is that it doesn’t serialize. But you could serialize the data with int/float, and perform everything else as decimal.

In C#, an int and a float are both 4 bytes. int has a range from 0 to 4,294,967,295 (or, if using it as cents, $42,949,672.95). I think you’ll be alright…

(If you think it’s possible that your player will have more than $43 million, then floats wouldn’t help you anyway - it can only go that high by losing precision on the other end.)

Also, these are minuscule amounts of memory we’re talking about here. If you have to store a million prices, you’d still only be taking about as much memory as a single 512x512 texture.

1 Like

How many accounts are we talking about here? You’d be hard pressed to run out of memory at 4 bytes a pop.

Adding to what StarManta is saying, if 43 million isn’t enough, use long.

8 byte long will give you a maximum of $92,233,720,368,547,758.07, which, according to google, is roughly 340 times the amount of money in the whole world.

If 92 quadrillion dollars wont cut it, then your in-game currency is over-inflated.

2 Likes

… or you’re creating a cookie clicker.

2 Likes

You say that as if clicking cookies doesn’t over-inflate currency…

We got a winner. I am making an Incremental game I can not have my number be restricted to int32.max_value I need something bigger, and my default has always been float. I’ve looked into it though, are there any issues with using double?

If it’s truly going to be a massive number, just use a long? Integral numeric types | Microsoft Learn.

It’ll go all the way from –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.

float and double
Float and double are similar, one is just 32-bit, the other 64-bit.

They are both capable of storing fractional values. BUT, they’re stored in binary. Many normal base 10 fractions are repeating values in binary. For instance 0.1 is 0.00011001100110011 repeating. Furthermore they have a smaller sig value range than the range of the number.

There ranges are controlled by their bit depth as well, with float going as high as 3.4*10^38, and double going as far up as 1.7 * 10^308.

BUT, even though they have a large range. They can’t store a number that is both large and small at the same time. This is the sig value range.

506000000000 has a sig value range of 3, the 506
506000000000.1 has a sig vlaue range of 13, the entirity of the number

Float has a sig value range of about 7 decimal digits (it’s not exact because really it’s 24 binary digits, which doesn’t translate directly). And a double has a sig value range of just shy of 16 decimal digits (53 binary digits).

If you need ‘precision’, these guys are NOT a good choice.
These are great for fast arithmetic with fractional values and large ranges, when precision is not necessary. In a video game, we usually don’t care about precision.

int and long
int and long are similar, one is just 32-bit, the other 64-bit

They store only whole numbers though. No fractions. Because of this though, there is not exponential information for the floating value. This means their sig value gets to take up the entirity of the number.

int gives you a little over 9 sig values
long gives you a just shy of 19 sig values

There are no precision issues, nor is their repeating value issues. This is primarily because you can’t store fractional values.

If you need precision, but don’t need fractional values, these guys are your choice.

decimal

This guy is a bit of both worlds.

It’s a 128-bit floating point value. BUT it’s exponential value is in base 10, as opposed to base 2 (binary).

Furthermore, they don’t let the exponent range float around as much. To maintain a range that is also in the precision range.

As a result you get 28 digits of sig value, and an upper range 7.9 10^28.
If you required say 2 digits of fractional sig value (for money), you only get an upper restriction drop of 2 sig values. 7.9
10^26

This means you get a larger range than long, but fractional values, all without the binary to decimal floating error (0.1 == 0.1 in decimal).

Honeslty, in my opinion, for very large number storage that needs both precision and fractional values for things like money… this is the best option. The decimal type was chosen by Microsoft for money, and that’s why its key symbol is M. And is based on the 64-bit version of it from VB6 called the ‘Currency’ data type.

decimal value = 10.05M;

But unfortunately ‘decimal’ is not serializable by unity directly.

You could of course write a serializable version, which I have one of here, with all the operators and conversion methods defined as well. So that way you can treat it like any other number.

And the PropertyDrawer:

Unfortunately the propertydrawer uses the double field, so the inspector is restricted back to double ranges. A more robust inspector could be written.

4 Likes

Wonderful! Thank you very much!