本文介绍了定点运算的C程序设计的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个存储股价高precision的应用程序。目前我使用了双这样做。为了节省多达记忆,我可以使用任何其他数据类型?我知道这事做与定点算法,但我不能弄明白。

I am trying to create an application that stores stock prices with high precision. Currently I am using a double to do so. To save up on memory can I use any other data type? I know this has something to do with fixed point arithmetic, but I can't figure it out.

推荐答案

背后定点运算的想法是,你存储乘以一定的值,使用乘积值的所有积分,且由同一把它当你想要的结果数量。这种技术的目的是利用整数运算(INT,长... ...),同时能够重新present分数。

The idea behind fixed-point arithmetic is that you store the values multiplied by a certain amount, use the multiplied values for all calculus, and divide it by the same amount when you want the result. The purpose of this technique is to use integer arithmetic (int, long...) while being able to represent fractions.

用C这样做的通常的和最有效的方法是使用位转移操作(LT;<和>>)。 2在每班除(>>)的整数值移位位是pretty该死的简单,快捷操作的ALU和这样做有乘以财产(小于;&LT)。当然,缺点是乘数必须是2的幂(这通常不是问题本身,因为我们真的不关心这个确切的倍数值)。

The usual and most efficient way of doing this in C is by using the bits shifting operators (<< and >>). Shifting bits is a pretty damn simple and fast operation for the ALU and doing this have the property to multiply (<<) and divide (>>) the integer value by 2 on each shift. Of course, the drawback is that the multiplier must be a power of 2 (which is usually not a problem by itself as we don't really care about that exact multiplier value).

现在让我们说,我们要使用32位整型来存储我们的价值观。我们必须选择2倍增的力量。让我们分蛋糕一分为二,所以说65536(这是最常见的情况,但你可以真正使用任何电源的2取决于precision你的需求)。这是2 和16在这里表示,我们将使用至少16位显著(LSB)的小数部分。的其余部分。(32 - 16 = 16)为最显著位(MSB),的整数部分

Now let's say we want to use 32 bits integers for storing our values. We must choose a power of 2 multiplier. Let's divide the cake in two, so say 65536 (this is the most common case, but you can really use any power of 2 depending on your needs in precision). This is 2 and the 16 here means that we will use the 16 least significant bits (LSB) for the fractional part. The rest (32 - 16 = 16) is for the most significant bits (MSB), the integer part.

     integer (MSB)    fraction (LSB)
           v                 v
    0000000000000000.0000000000000000

让我们把这种在code:

Let's put this in code:

#define SHIFT_AMOUNT 16 // 2^16 = 65536
#define SHIFT_MASK ((1 << SHIFT_AMOUNT) - 1) // 65535 (all LSB set, all MSB clear)

int price = 500 << SHIFT_AMOUNT;

这是你必须把在商店(结构,数据库什么的,)的值。注意,int是用C不一定32位,即使它是主要的情况时下。还没有进一步的声明,则默认情况下签署的。您可以添加未签名的声明是肯定的。更重要的是,你可以使用uint32_t的或uint_least32_t(在stdint.h定义),如果你的code高度依赖于整数位大小(你可能会出台一些黑客吧)。如有疑问,使用typedef为您的定点类型,你是安全的。

This is the value you must put in store (structure, database, whatever). Note that int is not necessarily 32 bits in C even though it is mostly the case nowadays. Also without further declaration, it is signed by default. You can add unsigned to the declaration to be sure. Better than that, you can use uint32_t or uint_least32_t (defined in stdint.h) if your code highly depends on the integer bit size (you may introduce some hacks about it). In doubt, use a typedef for your fixed-point type and you're safer.

当你想要做这个值演算,可以使用4种基本运算符:+, - ,*,/和。你必须记住,加减数值时(+和 - ),该值也必须移位。比方说,我们要添加10〜500我们的价格:

When you want to do calculus on this value, you can use the 4 basic operators: +, -, * and /. You have to keep in mind that when adding and subtracting a value (+ and -), that value must also be shifted. Let's say we want to add 10 to our 500 price:

price += 10 << SHIFT_AMOUNT;

但乘法和除法(*和/),乘数/除数一定不能移动。比方说,我们希望通过3乘:

But for multiplication and division (* and /), the multiplier/divisor must NOT be shifted. Let's say we want to multiply by 3:

price *= 3;

现在我们让事情更有趣除以4的价格,所以我们做了一个非零小数部分:

Now let's make things more interesting by dividing the price by 4 so we make up for a non-zero fractional part:

price /= 4; // now our price is ((500 + 10) * 3) / 4 = 382.5

这是所有关于规则。当您要检索的任何一点的真实价格,你必须右移:

That's all about the rules. When you want to retrieve the real price at any point, you must right-shift:

printf("price is %d\n", price >> SHIFT_AMOUNT);

如果您需要的小数部分,则必须将其屏蔽了:

If you need the fractional part, you must mask it out:

printf ("price fraction is %d\n", price & SHIFT_MASK);

当然,这个值是不是我们可以调用一个小数,实际上它的范围是[0 - 65535]一个整数。但它与小数范围[ - 0.9999 ... 0]准确映射。换句话说,映射看起来像:0 => 0,32768 => 0.5,65535 => 0.9999 ....一个简单的方法来显示它作为一个小数是诉诸到C内置此时浮动算法:

Of course, this value is not what we can call a decimal fraction, in fact it is an integer in the range [0 - 65535]. But it maps exactly with the decimal fraction range [0 - 0.9999...]. In other words, mapping looks like: 0 => 0, 32768 => 0.5, 65535 => 0.9999.... An easy way to show it as a decimal fraction is to resort to C built-in float arithmetic at this point:

printf("price fraction in decimal is %f\n", ((double)(price & SHIFT_MASK) / (1 << SHIFT_AMOUNT)));

这背后定点算术的基本思路。

These are the basic ideas behind fixed-point arithmetics.

小心负值。它有时变得非常棘手,尤其是当它的时间来显示最终值。此外,C是实现定义大约有符号整数(即使平台在哪里,这是一个问题,是非常罕见时下)。你应该总是让最小的测试,在您的环境,以确保一切都如预期。如果没有,你可以围绕它破解,如果你知道你做什么(我不会在这个发展,但这事做算术移比逻辑移位,2的补再presentation)。然而随着无符号整数,你大多是安全的不管你做什么的行为也有定义。

Be careful with negative values. It can becomes tricky sometimes, especially when it's time to show the final value. Besides, C is implementation-defined about signed integers (even though platforms where this is a problem are very uncommon nowadays). You should always make minimal tests in your environment to make sure everything goes as expected. If not, you can hack around it if you know what you do (I won't develop on this, but this has something to do with arithmetic shift vs logical shift and 2's complement representation). With unsigned integers however, you're mostly safe whatever you do as behaviors are well defined anyway.

还注意到,如果一个32位整数无法重新present值大于2 - 1,采用定点运算与2 限制你的范围为2 - 1! (和符号整数除以所有这一切都通过2,在我们的例子让我们为2的可用范围 - 1)。我们的目标是,然后选择合适的情况SHIFT_AMOUNT。这是整数部分的幅度和小数部分precision之间的折衷。

Also take note that if a 32 bits integer can not represent values bigger than 2 - 1, using fixed-point arithmetic with 2 limits your range to 2 - 1! (and divide all of this by 2 with signed integers, which in our example would leave us with an available range of 2 - 1). The goal is then to choose a SHIFT_AMOUNT suitable to the situation. This is a tradeoff between integer part magnitude and fractional part precision.

现在为真正的警告:这个技术是绝对不适合所在的地区precision是重中之重(金融,科技,军事......)。通常的浮点(浮点/双精度)也往往不是precise就够了,即使他们比总体定点更好的性能。定点具有相同precision任何的值(这可以是在某些情况下,一个优点),其中,漂浮precision成反比的值幅度(即幅度越低,越precision你......嗯,这是比这更复杂,但你明白了吧)。还漂浮具有比等效大得多幅度(以比特数)的整数(定点与否),到具有高值的precision的损失的成本(甚至可以达到数量级的一个点,加入1或更大的值将没有任何效果可言,这东西不能用整数发生)。

Now for the real warnings: this technique is definitely not suitable in areas where precision is a top priority (financial, science, military...). Usual floating point (float/double) are also often not precise enough, even though they have better properties than fixed-point overall. Fixed-point has the same precision whatever the value (this can be an advantage in some cases), where floats precision is inversely proportional to the value magnitude (ie. the lower the magnitude, the more precision you get... well, this is more complex than that but you get the point). Also floats have a much greater magnitude than the equivalent (in number of bits) integers (fixed-point or not), to the cost of a loss of precision with high values (you can even reach a point of magnitude where adding 1 or even greater values will have no effect at all, something that cannot happen with integers).

如果您在这些领域懂事的工作,你就要去利用专用于任意precision的目的库(去看看,它是免费的)。在计算科学,本质上,获得precision是你用来存储值的位数。你要高precision?使用位。这就是全部。

If you work in those sensible areas, you're better off using libraries dedicated to the purpose of arbitrary precision (go take a look at gmplib, it's free). In computing science, essentially, gaining precision is about the number of bits you use to store your values. You want high precision? Use bits. That's all.

这篇关于定点运算的C程序设计的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-14 03:21