Update
parent
556653d6e4
commit
eb91ebfbc9
@ -1,29 +1,106 @@
|
||||
# Fixed Point
|
||||
|
||||
Fixed point arithmetic is a simple and often [good enough](good_enough.md) way of dealing with fractional (non-integer) numbers, as opposed to [floating point](float.md) which is from our point of view considered a bad solution for most programs.
|
||||
|
||||
Probably in 99% cases when you think you need floating point, fixed point is actually what you need.
|
||||
Fixed point arithmetic is a simple and often [good enough](good_enough.md) representation of [fractional](rational_number.md) (non-integer) numbers, as opposed to [floating point](float.md) which we consider a bad, [bloated](bloat.md) alternative (in most cases). Probably in 99% cases when you think you need floating point, fixed point is actually what you need.
|
||||
|
||||
Fixed point has at least these advantages over floating point:
|
||||
|
||||
- **It doesn't require a special hardware coprocessor** for efficient execution and so doesn't introduce a [dependency](dependency.md). Programs using floating point will run extremely slowly on systems without float hardware support as they have to emulate the complex hardware in software, while fixed point will run just as fast as integer arithmetic. For this reason fixed point is very often used in [embedded](embedded.md) computers.
|
||||
- It is **easier to understand and better predictable**, less tricky, [KISS](kiss.md), [suckless](sukless.md). (Float's IEEE 754 standard is 58 pages long, the paper *What Every Computer Scientist Should Know About Floating-Point Arithmetic* has 48 pages.)
|
||||
- **Doesn't require a special hardware coprocessor** and so doesn't introduce a [dependency](dependency.md). Programs using floating point will run extremely slowly on systems without float hardware support as they have to emulate the complex hardware in software, while fixed point will run just as fast as integer arithmetic.
|
||||
- Is easier to implement and so **supported in many more systems**. Any language or format supporting integers also supports fixed point.
|
||||
- Isn't ugly and doesn't waste values (positive and negative zero, denormalized numbers, ...).
|
||||
|
||||
## How It Works
|
||||
|
||||
Fixed point uses some fixed (hence the name) number of digits (bits in binary) for the fractional part (whereas floating point's fractional part varies in length).
|
||||
Fixed point uses a fixed (hence the name) number of digits (bits in binary) for the integer part and the rest for the fractional part (whereas floating point's fractional part varies in size). I.e. we split the binary representation of the number into two parts (integer and fractional) by IMAGINING a radix point at some place in the binary representation. That's basically it.
|
||||
|
||||
So for example when working with 16 bit numbers, we may choose to use 12 bits for the integer part and the remaining 4 for the fractional part. This puts an imaginary radix point after the first (highest) 12 bits of the number in binary representation, like this:
|
||||
So, **we can just use an integer data type as a fixed point data type**, there is no need for libraries or special hardware support. We can also perform operations such as addition the same way as with integers. For example if we have a binary integer number represented as `00001001`, 9 in decimal, we may say we'll be considering a radix point after let's say the sixth place, i.e. we get `000010.01` which we interpret as 2.25 (2^2 + 2^(-2)). The binary value we store in a variable is the same (as the radix point is only imagined), we only INTERPRET it differently.
|
||||
|
||||
We may look at it this way: we still use integers but we use them to count smaller fractions than 1. For example in a 3D game where our basic spatial unit is 1 meter our variables may rather contain the number of centimeters (however in practice we should use powers of two, so rather 1/128ths of a meter). In the example in previous paragraph we count 1/4ths (we say our **scaling factor** is 1/4), so actually the number represented as `00000100` is what in floating point we'd write as `1.0` (`00000100` is 4 and 4 * 1/4 = 1), while `00000001` means `0.25`.
|
||||
|
||||
This has just one consequence: **we have to [normalize](normalize.md) results of multiplication and division** (addition and subtraction work just as with integers, we can normally use the `+` and `-` operators). I.e. when multiplying, we have to divide the result by the inverse of the fractions we're counting, i.e. by 4 in our case (1/(1/4) = 4). Similarly when dividing, we need to MULTIPLY the result by this number. This is because we are using fractions as our units and when we multiply two numbers in those units, the units multiply as well, i.e. in our case multiplying two numbers that count 1/4ths give a result that counts 1/16ths, we need to divide this by 4 to get the number of 1/4ths back again (this works the same as e.g. units in physics, multiplying number of meters by number of meters gives meters squared.) For example the following integer multiplication:
|
||||
|
||||
`00001000` * `00000010` = `00010000` (8 * 2 = 16)
|
||||
|
||||
in our system has to be normalized like this:
|
||||
|
||||
(`000010.00` * `000000.10`) / 4 = `000001.00` (2.0 * 0.5 = 1.0)
|
||||
|
||||
With this normalization we also have to **think about how to bracket expressions** to prevent rounding errors and [overflows](overflow.md), for example instead of `(x / y) * 4` we may want to write `(x * 4) / y`; imagine e.g. *x* being `00000010` (0.5) and *y* being `00000100` (1.0), the former would result in 0 (incorrect, rounding error) while the latter correctly results in 0.5. The bracketing depends on what values you expect to be in the variables so it can't really be done automatically by a compiler or library (well, it might probably be somehow handled at [runtime](runtime.md), but of course, that will be slower).
|
||||
|
||||
The normalization is basically the only thing you have to think about, apart from this everything works as with integers. Remember that **this all also works with negative number in [two's complement](twos_complement.md)**, so you can use a signed integer type without any extra trouble.
|
||||
|
||||
Remember to **always use a power of two scaling factor** -- this is crucial for performance. I.e. you want to count 1/2th, 1/4th, 1/8ths etc., but NOT 1/10ths, as might be tempting. Why are power of two good here? Because computers work in binary and so the normalization operations with powers of two (division and multiplication by the scaling factor) can easily be optimized by the compiler to a mere [bit shift](bit_shift.md), an operation much faster than multiplication or division.
|
||||
|
||||
## Implementation Example
|
||||
|
||||
The following is an example of a simple [C](c.md) program using fixed point with 10 fractional bits, computing [square roots](sqrt.md) of numbers from 0 to 10.
|
||||
|
||||
```
|
||||
abcdefghijkl mnop
|
||||
------------.----
|
||||
```
|
||||
#include <stdio.h>
|
||||
|
||||
The whole number `abcdefghijkl` can take on values from 0 to 4095 (or -2048 to 2047 when using [two's complement](twos_complement.md) -- yes, fixed point works as signed type too). The fraction can take 16 (2^(4)) values, meaning we subdivide every whole number into 16 parts (we say our *scaling factor* is 1/16).
|
||||
typedef int Fixed;
|
||||
|
||||
You can also imagine it like this: when you e.g. write a 3D game and measure your dimensions in meters, fixed point just means considering a denser grid; so you just say your variables will count centimeters rather than meters and everything will just work. (Practically however you wouldn't use centimeters but power of 2 subdivisions, e.g. 256ths of a meter.)
|
||||
#define UNIT_FRACTIONS 1024 // 10 fractional bits, 2^10 = 1024
|
||||
|
||||
TODOOOOOOOOO
|
||||
#define INT_TO_FIXED(x) ((x) * UNIT_FRACTIONS)
|
||||
|
||||
Fixed fixedSqrt(Fixed x)
|
||||
{
|
||||
// stupid brute force square root
|
||||
|
||||
int previousError = -1;
|
||||
|
||||
for (int test = 0; test <= x; ++test)
|
||||
{
|
||||
int error = x - (test * test) / UNIT_FRACTIONS;
|
||||
|
||||
if (error == 0)
|
||||
return test;
|
||||
else if (error < 0)
|
||||
error *= -1;
|
||||
|
||||
if (previousError > 0 && error > previousError)
|
||||
return test - 1;
|
||||
|
||||
previousError = error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fixedPrint(Fixed x)
|
||||
{
|
||||
printf("%d.%03d",x / UNIT_FRACTIONS,
|
||||
((x % UNIT_FRACTIONS) * 1000) / UNIT_FRACTIONS);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
for (int i = 0; i <= 10; ++i)
|
||||
{
|
||||
printf("%d: ",i);
|
||||
|
||||
fixedPrint(fixedSqrt(INT_TO_FIXED(i)));
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
The output is:
|
||||
|
||||
```
|
||||
0: 0.000
|
||||
1: 1.000
|
||||
2: 1.414
|
||||
3: 1.732
|
||||
4: 2.000
|
||||
5: 2.236
|
||||
6: 2.449
|
||||
7: 2.645
|
||||
8: 2.828
|
||||
9: 3.000
|
||||
10: 3.162
|
||||
```
|
@ -0,0 +1,9 @@
|
||||
# Whale
|
||||
|
||||
In online [pay to win](pay_to_win.md) [games](game.md) a whale is a player who spends enormous sums of money, much more than most of other players combined. They buy the most expensive items in the game stores daily, they highly engage in [microtheft](microtheft.md) and may throw even thousands of dollars at the game every day (cases of players spending over 1 million dollars on a casual game are known). In the playerbase there may exist just a handful of whale players but those are normally the main source of income for the game so that the developers actually focus almost exclusively on those few players, they craft the game to "catch the whales". The income from the rest of the players is negligible -- nevertheless the non-whales also play an important role, they are an attraction for the whales, they are there so that they can be owned by the whale players.
|
||||
|
||||
Existence of whale players is one of the demonstrations of the [pareto principle](pareto.md) (80/20 rule): 80% of the game's income comes from 20% of the players, out of which, by extension, 80% again comes from the 20% and so on.
|
||||
|
||||
The terminology can be extended further: besides **whales** we may also talk about **dolphins** (mid-spending players) and **minnows** (low spending players). Extreme whales are sometimes called **white whales** or **super whales** (about 0.2% generating 50% of income).
|
||||
|
||||
In some games, such as [WoW](wow.md), players may buy multiple accounts and practice so called [multiboxing](multiboxing.md). This means they control multiple characters at once, often using [scripts](script.md) to coordinate them, which of course gives a great advantage. Though using scripts or "hacking" the game in similar ways is in other cases considered unacceptable [cheating](cheating.md) that results in immediate ban, for obvious reasons (money) developers happily allow this -- of course this just shows they don't give a single shit about fairness or balance, they only thing they care about is $$$profit$$$.
|
Loading…
Reference in New Issue