2009-11-20 60 views
2

我试图在C++中表示一组简单的3个概率。例如:在C++中表示概率

a = 0.1 
b = 0.2 
c = 0.7 

(据我所知概率加起来必须等于1)

我的问题是,当我试图代表用C 0.7 ++为float我结束了0.69999999,从而赢得当我稍后做我的计算时没有帮助。 0.8,0.80000001一样。

在C++中有更好的表示0.0到1.0之间数字的方法吗?

请记住,这涉及如何将数字存储在内存中,以便在对数值进行测试时他们是正确的,我不关心它们如何显示/打印出来。

+2

强制性链接:http://docs.sun.com/source/806-3568/ncg_goldberg.html – robince 2009-11-20 19:23:29

回答

8

你需要多少精度?您可能会考虑缩放值并以定点表示方式量化它们。

22

这与C++无关,所有与浮点数如何在内存中表示有关。你永远不应该使用等于运算符来比较浮点值,在这里看到更好的方法:http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

+2

他没有问C++的方法而言,他并没有要求相关比较浮点值什么。 – 2009-11-20 19:43:24

+1

是的,他做到了。 “......所以当涉及到对这些值进行测试时,他们是正确的”。我将其解释为:将这些值与其他值进行比较。 – lyricat 2009-11-20 19:51:18

+0

有许多方法可以“测试”不涉及使用相等运算符的浮点“值”。无论如何,这不是他的问题。 – 2009-11-20 20:23:30

14

我的问题是,当我尝试 在C表示0.7 ++为float我最终 了0.69999999,这当我稍后进行我的计算时,将不会帮助 。 同样为0.8,0.80000001。

这是真的吗?如果您只需要更高的精度,请使用double而不是float。这应该会让你获得15位数字的精度,对于大多数工作来说绰绰有余。

考虑你的源数据。 0.7真的比0.69999999更正确吗?

如果是这样,你可以使用一个合理的数字图书馆,例如:如果问题是,概率的定义加起来1

http://www.boost.org/doc/libs/1_40_0/libs/rational/index.html

,然后将它们存储为数字集合,省略最后一个。通过从1中减去其他值的总和来推断最后一个值。

+1

将float精度从float更改为double并不能解决问题,只是推迟它。 – hirschhornsalz 2009-11-20 19:15:54

+0

有很多应用程序的浮点数不够精确,但双重就足够了。 – 2009-11-20 19:48:35

+0

我喜欢上一个值为“1 - sum(所有其他值)” – 2009-11-20 21:03:04

1

如果您只需要几位数的精度,那么只需使用一个整数。如果您需要更高的精度,那么您将不得不寻找能够提供精度保证的不同库。

1

二进制机器总是将十进制小数(除了.0和.5,.25,.75等)舍入为在浮点中没有精确表示的值。这与语言C++无关。除了从代码中的数字角度处理它外,没有真正的解决方法。

至于实际制作你所寻求的概率:

float pr[3] = {0.1, 0.2, 0.7}; 
float accPr[3]; 
float prev = 0.0; 
int i = 0; 

for (i = 0; i < 3; i++) { 
    accPr[i] = prev + pr[i]; 
    prev = accPr[i]; 
} 

float frand = rand()/(1 + RAND_MAX); 
for (i = 0; i < 2; i++) { 
    if (frand < accPr[i]) break; 
} 
return i; 
+0

“的意思,除了.0和.5”可能是误导或错误的,这取决于您想如何看待它。 – 2009-11-20 19:28:28

+0

你是对的,我应该说分母的分母是2的幂。 – 2009-11-20 23:38:26

2

如果你确实需要的精确度,并与有理数坚持,我想你可以用一个固定的点arithemtic去。我以前没有这样做过,所以我不推荐任何库。

或者,你可以比较FP数字时设置的阈值,但是你必须宁可一侧或另一侧 - 说

bool fp_cmp(float a, float b) { 
    return (a < b + epsilon); 
} 

注意,过量的精度在每个计算自动截断,因此在算法中以多种不同的数量级进行操作时,应该小心。一个人为的例子来说明:

a = 15434355e10 + 22543634e10 
b = a/1e20 + 1.1534634 
c = b * 1e20 

c = b + 1.1534634e20 

两个效果会非常不同。使用第一种方法,前两个数字的许多精度将会在除以1e20时丢失。假设你想要的最终数值是1e20的数量级,第二种方法会给你更高的精度。

+0

为什么你不会写: return(a micmoo 2009-11-20 21:08:03

+0

......哎呀,多么尴尬。我已经看过这样的代码几次,甚至嘲笑它。我想我没在想。 – int3 2009-11-20 21:18:51

+0

并据此编辑。 – int3 2009-11-20 21:19:22

2

你想用你的号码做的测试将是不正确的。

对于像0.1这样的数字,在基数为2的数字系统中没有精确的浮点表示,因为它是一个infinite周期数。考虑三分之一,在base-3系统中可以精确表示为0.1,但在base-10系统中则为0.333 ...。

所以,你用浮点数0.1做的任何测试都容易出现缺陷。

中的溶液。将使用有理数(升压有其合理IIb)的,这将是始终确切为,ermm,有理数,或由数字与10的幂乘以使用自制基-10系统。

-5

是的,如果你担心这样的事情,我会扩大数字(0-100)(0-1000)或任何你需要的固定大小。它也使得在大多数情况下更快的数学计算。回到古老的时代,我们会定义整个cos/sine表和其他整数形式的bleh,以减少浮动模糊并提高计算速度。

我的确发现有点“有趣”,就像存储上的“0.7”模糊不清。

+1

7/10模糊类似base-2的表示,就像1/3或1/7模糊的base-10表示。没有什么不奇怪的。 – 2009-11-20 19:27:53

+0

真的很糟糕的答案。这应该是你的“最佳答案” – 2009-11-21 02:48:28

+0

*适用时*这是一个完全合理的答案,应该不是唯一的选择,而是作为一个可能有效的答案,因此downvotes是没有根据的。 @Roberto:关键是0.7可以被存储为700,因为知道1的概率是由1000表示的 - 精确的7/10的浮点可表示性是不相关的。 – 2011-05-25 03:32:25

0

我很抱歉地说你的问题并不是一个简单的答案。

它属于一个名为"Numerical Analysis"的研究领域,它处理这些类型的问题(远远不只是确保您不检查两个浮点值之间的相等性)。而通过研究领域,我的意思是有大量的书籍,期刊文章,课程等处理它。有些人在做博士论文。

我只能说我很感谢我不必非常处理这些问题,因为问题和解决方案往往非常不直观。

你可能需要做些什么来处理表示你正在处理的数字和计算,这取决于你正在做什么操作,这些操作的顺序以及你期望处理的值的范围在那些行动中。

1

的这里的问题是,浮点数被存储在基地2.基地2

不能完全代表以10为基数的十进制与浮点数让我们退一步第二。 .1是什么意思?还是.7?它们的意思是1x10 -1和7x10 -1。如果您使用binary作为您的电话号码,而不是像我们通常所做的那样使用基数10,则.1表示1x2 -1或1/2。 .11表示1×2 -1 + 1×2 -2或1/2 + 1/4或3/4。

请注意,在这个系统中,分母总是2的幂。如果没有分母,那么在有限数目的数字中是2的幂。例如,.1(十进制)意味着1/10,但是在二进制中,它是一个无限重复分数,0.000110011 ...(0011模式永远重复)。这与基数10中的相似,1/3是无限分数,0.3333 ....;基数10只能用一个分母来表示数字,它是2和5的倍数的分母。(顺便说一下,基数12和基数60实际上是非常方便的基数,因为12可以被2,3和4整除,并且60可以被2,3,4和5整除;但由于某种原因,我们使用十进制无论如何,我们在计算机中使用二进制)。由于浮点数(或定点数)总是有限数字的数字,所以它们不能准确地表示这些无限重复的分数。因此,它们要么将值缩短或舍入为尽可能接近实际值,但不完全等于实际值。一旦你开始添加这些四舍五入的值,你开始得到更多的错误。在十进制中,如果您的1/3表示为.333,那么三个副本将加起来为.999,而不是1.

有四种可能的解决方案。如果你所关心的只是像.1和.7这样的小数部分(如你所说的,你不关心1/3会遇到同样的问题),那么你可以用十进制表示你的数字,例如使用binary coded decimal,并操纵这些。这是金融领域的常见解决方案,许多操作都是用小数定义的。这有一个缺点,你需要自己实现所有的算术运算,而没有计算机的FPU的好处,或者找到一个decimal arithmetic library。如前所述,这也不能帮助那些不能完全用小数表示的分数。

另一种解决方案是使用分数来表示您的数字。如果你使用分数和分数(任意大数)作为分子和分母,你可以表示任何适合你计算机内存的理性数。同样,缺点是算术运算速度会变慢,您需要自己实现算术运算或use an existing library。这将解决所有有理数的问题,但是如果用基于π或√2计算的概率结束,则仍然存在相同的问题,因为无法准确表示它们,并且还需要使用一个以后的解决方案。

第三个解决方案,如果所有你关心的是让你的号码加起来正好1,是,你必须ñ可能性,只存储ň这些概率的-1值的事件,并计算最后的概率为1减去其余概率的总和。

第四种解决方案是在处理浮点数(或任何不精确的数字,例如用于表示无理数的分数)时始终需要记住的事项,并且从不比较两个数的相等性。再次在基数10中,如果您合计三份1/3,您将以.999结尾。当你想把这个数字与1进行比较时,你必须进行比较,看它是否接近1;检查差值的绝对值1-.999是否小于阈值,如.01。

0

根据您的应用需求了几种解决方案中的任何一个可能是最好的:

  1. 你住与先天就缺乏精度和使用浮点或双精度的。你不能测试是否相等,这意味着你不能测试你的概率与1.0相等的总和。

  2. 如前所述,如果您需要固定精度,则可以使用整数。您将0.7表示为7,0.1表示为1,0.2表示为2,它们将完全加起来为10,即1.0。如果必须用概率进行计算,特别是在进行除法和乘法运算时,则需要正确地舍入结果。这将再次引入不准确。

  3. 将您的数字表示为具有一对整数(1,2)= 1/2 = 0.5的分数。精确,比2更灵活),但你不想用这些计算。

  4. 您可以一路走下来,并使用实现有理数的库(例如gmp)。精确的,任意的精度,你可以用它来计算,但速度很慢。