Five ways to write Unity/C# rounding

Five Ways to Write Rounding

0. Introduction

Rounding is a mathematical concept, a rounding off rule.

In daily life, in order to simplify the format and facilitate memory, we often use rounding methods to remove fractions or round up integers to solve such problems.

In game development, rounding is also an important part of numerical calculations. Only by using the correct rounding rules, can we cooperate with the numerical planning to carry out more reasonable numerical design and calculation, so that players can recognize the rationality of the game numerical values ​​without feeling as much as possible.

However, when using Unity for development, we found that the result of Range in Unity is not the same as the rounding result used in daily use.

This is because there are other different rounding rules besides the rounding method of rounding, which also have their own unique definitions in mathematics.

This article will start with the C# language used by the Unity engine and explain the five ways of rounding.

0.1 rounding definition

When calculating a number with a large number of digits, for convenience or due to the limitation of calculation tools, it is necessary to replace it with a number with a small number of digits (sometimes it is not necessary to calculate all the numbers according to the requirements of accuracy).

Therefore, it is necessary to remove the number after a certain effective figure of a number according to certain rules, and adjust the remaining part to make it as close as possible to the original number. This process is called rounding.

0.2 Definition of rounding error

Rounding error is the difference between the approximate and exact value of an operation.

For example, when a floating-point number with a finite number of digits is used to represent a real number (in theory, there is a floating-point number with an infinite number of digits), rounding errors will occur.

Roundoff error is a form of quantization error.

If a round-off error occurs in one or several steps in a series of operations, in some cases, the error will accumulate as the number of operations increases, and finally a meaningless operation result will be obtained.

Examples of rounding errors:

Increasing the number of digits can reduce rounding errors that may occur, but the number of digits is limited, and errors will still occur when representing infinite floating-point numbers.

In the case of floating-point numbers represented by conventional methods, this error is unavoidable, but it can be reduced by setting a warning bit.

Multi-step rounding will increase the rounding error. For example, the number 9.945309 is rounded to two decimal places (9.95) when entered, and then rounded to one decimal place (10.0) when displayed. The rounding error is 0.054691. If the original number is only rounded to one decimal place (9.9) in one step, the rounding error is only 0.045309.

1 round to nearest

Rounds the original number to the nearest, specified precision.

For positive numbers:

  • If the next digit is from 0 to 4, the nearest digit is negative infinity.

  • If the next digit is from 6 to 9, the nearest digit is positive infinity.

For negative numbers:

  • If the next number is from 0 to 4, the nearest number is positive infinity.

  • If the next digit is from 6 to 9, the nearest digit is negative infinity.

Then the really controversial number is only 5, so the two rounding rules for rounding to the nearest are the rounding rules for when the number is 5.

1.1 Far zero rounding Away From Zero

1.1.1 Rounding Rules

Rule: When a number is halfway between two numbers, it rounds to the nearest number from zero.

Far-zero rounding is what we are familiar with "rounding up"

1.2 Near-even rounding To Even

1.2.1 Rounding Rules

Rule: When a number is halfway between two numbers, it rounds to the nearest even number.

Near-even rounding is also called "banker's rounding", or "rounding to six and five to two".

When it comes to rounding, people in our age group should be very clear, because our primary school education at that time was instilled in rounding. But when it comes to banker's rounding, maybe many friends will be stunned for a while. Banker's rounding, the English name is Banker's round, and the rounding effect it achieves is "rounding up to five evens".

Banker's rounding is one of the decimal rounding standards specified by IEEE, and it is also the best rounding method currently specified by IEEE. Therefore, all languages ​​that conform to IEEE standards should implement this algorithm, and .NET platform and Unity are no exception.

1.2.2 Comparison of near-even rounding and far-zero rounding

First we compare their rules:

  • rounding

    • When the value of the discarded digit is greater than or equal to 5, while the digit is discarded, the forward digit is advanced by one
    • When the value of the discarded digit is less than 5, the digit is discarded directly
  • Near even rounding

  • The so-called banker's rounding method is, in essence, a method of rounding to five evens (also known as rounding to five and keeping doubles).

  • Its rules are:

    • When the value of the discarded digit is less than 5, discard the digit directly;
    • When the value of the discarded digit is greater than or equal to 6, while the digit is discarded, the forward digit is advanced by one
    • When the value of the rounded digit is equal to 5
      • If the value of the previous digit is odd, the previous digit is carried forward while discarding the digit
      • If the value of the first digit is even, the digit is discarded directly.

Example: We round and bankers round 2.335, 2.345, 2.364, 2.366, 2.367 respectively

Raw data rounding Near even rounding
2.335 2.34 2.34
2.345 2.35 2.34
2.364 2.36 2.36
2.366 2.37 2.37
2.367 2.37 2.37

How do you compare them?

For a series of numbers 1, 2, 3, 4, 5, 6, 7, 8, and 9, the random probability of their appearance is almost the same.

Therefore, if rounding is used for rounding, then the balance of parity on both sides around 5 is not good, 5 to 9 are carried, and 1 to 4 are discarded.

If you use the banker's algorithm, 1 to 4 are discarded, 6 to 9 are carried, each has four digits, and then the 5 is divided into two cases, if the first digit is odd, it will be even, if it is even, it will be kept. We found that 5 is in front of It is exactly the same as the number of odd and even numbers after 5.

When the numerical precision is greater, the probability of rounding 5 is lower, and it is infinitely close to 0, that is to say, when the numerical precision is higher, the algorithm is more like "rounding".

The reality is that the precision of the value cannot be infinite. The deposit interest rate is generally a few tenths of a percent, and the precision of the value is generally 4 digits. The probability of non-zero numbers after 5 is relatively small, so it tends to be rounded to 1/2 5, 1/2 into 5

Therefore, compared with rounding, the banker's rounding at this time is more fair.

Small case of capitalists:

We know that the profit channel of banks is mainly the interest rate difference . They collect funds from depositors and then lend them out. The interest difference between them is the profit obtained. For a bank, the calculation of the interest paid to depositors is very frequent.

After the introduction of the scene, let’s look back at the rounding. Numbers less than 5 are rounded off, and numbers greater than or equal to 5 are rounded up. Since all digits are calculated naturally, we can see that the rounded The numbers are evenly distributed between 0 and 9. Let’s take 10 deposit interest calculations as a model and think about this algorithm as a banker:

(1) Rounding up: The value of discarding: 0.000, 0.001, 0.002, 0.003, 0.004, because it is discarding, for the banker, it is not necessary to pay the depositor, and every time a number is discarded, the corresponding amount will be earned: 0.000, 0.001, 0.002, 0.003, 0.004.

(2) Five-entry: Carry value: 0.005, 0.006, 0.007, 0.008, 0.009, because it is a carry, for the banker, every entry will pay more to the depositor, that is, a loss, and the loss part is Its corresponding decimal complement: 0.005, 0.004, 0.003, 0.002, 0.001

Because the discarded and rounded numbers are evenly distributed between 0 and 9, for the banker, the profit obtained by rounding up the interest of every 10 deposits is:

0.000 + 0.001 + 0.002 + 0.003 + 0.004 - 0.005 - 0.004 - 0.003 - 0.002 - 0.001 = -0.005

That is to say, there will be a loss of 0.005 yuan in every 10 interest calculations, that is, a loss of 0.0005 yuan per interest calculation

This algorithm error was discovered by American bankers, and a correction algorithm was proposed, which is called banker's rounding

"Banker's rounding" is the recommended rounding standard of the IEEE754 standard.

Therefore, all languages ​​that conform to the IEEE standard use this algorithm, and Unity and C# are no exception.

Compared with the usual rounding, this method can better maintain the characteristics of the original data in terms of the average.

2 directional rounding

Directional rounding does not care what the next digit of the exact digit is, it directly specifies rounding in a certain direction.

That is to say, the difference between directional rounding is mainly in the rounding direction.

There are three directions on the number axis:

  • +∞ positive infinity

  • 0 zero

  • -∞ negative infinity

Therefore, there are also three rules for directional rounding, corresponding to these three directions.

2.1 Round Up To Positive Infinity

Rules: upward directional rounding, the result is closest and not less than the infinitely exact result

2.2 Round Down To Negative Infinity

Rules: Downward-directed rounding, the result is closest to and not greater than the infinitely exact result.

2.3 Rounding To Zero towards zero

Rules: The strategy of rounding towards zero, the result is closest and the order of magnitude is not greater than the infinitely exact result.

3. Organize the summary table

rounding method rounding rules rounding feature Applicable scene Accuracy rounding case
Rounding
Away From Zero
When the number is halfway between two numbers
it will round to the nearest number from zero
The domestic rounding method Rounding scenarios that need to be understood by domestic users
(player-sensitive data selection)
generally 2.4≈2
2.5≈3
-2.4≈-2
-2.5≈-3
Near Even Rounding
To Even
It will round to the nearest even number when the number is halfway between two numbers In foreign countries, the rounding method used by Unity
is more uniform and more scientific than the far-zero rounding distribution under a large amount of data
All kinds of data that require precise trade-offs can be used
(calculated by various game numerical formulas)
high 2.4≈2
2.5≈2
-2.4≈-2
-2.5≈-2
3.4≈3
3.5≈4
-3.4≈-3
-3.5≈-4
Round Up
To Positive Infinity
Up Directed Rounding tends to be greater than the exact result Scenarios that require trade-offs and can only be estimated Low 2.4≈3
2.5≈3
-2.4≈-2
-2.5≈-2
Round Down
To Negative Infinity
Down Directed Rounding Often less than exact results Scenarios that require trade-offs and can only be underestimated Low 2.4≈2
2.5≈2
-2.4≈-3
-2.5≈-3
Round
To Zero
Strategies for rounding towards zero Often the order of magnitude will not be greater than the exact result Scenarios that only care about the data before the exact number of digits Low 2.4≈2
2.5≈2
-2.4≈-2 -2.5≈-2

4. Use these five roundings in Unity/C#

In the process of game development using Unity and C#, the Round class is generally used for rounding operations. And this class is actually two classes from different namespaces:

One is System.Math.Round from C#, and the other is UnityEngine.Mathf.Round from Unity engine.

4.1 UnityEngine.Mathf.Round

Let's take a look at advanced Unity, and the decompilation results are as follows:
insert image description here
insert image description here

You can see that Unity's Mathf only encapsulates the C# code, which is essentially C#'s Math.

Moreover, Mathf.Round can only be rounded, and cannot be rounded according to precision.

4.2 System.Math.Round

Let's take a look at the Round of C# again. The decompilation results are as follows:
insert image description here

The official documentation explains it as follows:
insert image description here

It can be seen that C#'s Round has more room for manipulation, and the parameter: MidpointRounding is an enumeration of the five rounding rules we mentioned earlier.

A method that does not pass this parameter will use the near-even rounding rule by default.

That is to say, using Unity Mathf.Round for rounding will use the near-even rounding rule by default.

This also explains our doubts at the beginning, why sometimes the rounding results using Unity are different from the rounding results.

The official documentation of the MidpointRounding enumeration states the following:

insert image description here

Code example:

decimal result;

// 正数情况

result = Math.Round(3.45m, 1, MidpointRounding.ToEven);
Console.WriteLine($"{result} = Math.Round({3.45m}, 1, MidpointRounding.ToEven)");
result = Math.Round(3.45m, 1, MidpointRounding.AwayFromZero);
Console.WriteLine($"{result} = Math.Round({3.45m}, 1, MidpointRounding.AwayFromZero)");
result = Math.Round(3.47m, 1, MidpointRounding.ToZero);
Console.WriteLine($"{result} = Math.Round({3.47m}, 1, MidpointRounding.ToZero)\n");

// 负数情况

result = Math.Round(-3.45m, 1, MidpointRounding.ToEven);
Console.WriteLine($"{result} = Math.Round({-3.45m}, 1, MidpointRounding.ToEven)");
result = Math.Round(-3.45m, 1, MidpointRounding.AwayFromZero);
Console.WriteLine($"{result} = Math.Round({-3.45m}, 1, MidpointRounding.AwayFromZero)");
result = Math.Round(-3.47m, 1, MidpointRounding.ToZero);
Console.WriteLine($"{result} = Math.Round({-3.47m}, 1, MidpointRounding.ToZero)\n");

/*
结果输出:

3.4 = Math.Round(3.45, 1, MidpointRounding.ToEven)
3.5 = Math.Round(3.45, 1, MidpointRounding.AwayFromZero)
3.4 = Math.Round(3.47, 1, MidpointRounding.ToZero)

-3.4 = Math.Round(-3.45, 1, MidpointRounding.ToEven)
-3.5 = Math.Round(-3.45, 1, MidpointRounding.AwayFromZero)
-3.4 = Math.Round(-3.47, 1, MidpointRounding.ToZero)
*/

5. References

MidpointRounding enumeration (System) | Microsoft Docs

Math.Round method (System) | Microsoft Docs

Rounding_Baidu Encyclopedia (baidu.com)

B station synchronous explanation video:
https://www.bilibili.com/video/BV1tV4y1p7aZ/

Guess you like

Origin blog.csdn.net/qq_44705559/article/details/126697308