在数学中,(实数)的加法是可交换的和关联的,即。对于所有数字x,y和z
x + y = y + x(交换性)
和
x +(y + z)=(x + y)+ z(相关性)
实数的乘法也是可交换和关联的。但这对于.NET中的int和浮点数是否正确?反例欢迎。
编辑:背景是我们最近并行化算法,现在它的结果在重复之间不再一致。我推测这可能是由于原子计算以非确定性顺序返回(并被合并)。在这种情况下,可以通过更智能(但更慢)的合并算法(在合并之前对结果进行排序)来修复不一致性。我想知道它可以对.NET算法做出什么假设。
答案 0 :(得分:9)
假设没有发生未经检查的溢出,.NET的整数加法和乘法(int
,long
等)是可交换的和关联的,就像数学中的实数一样。由于精度的限制,浮点运算(float
和double
)是可交换的,但并不总是精确关联的。来自Wikipedia (with an example in the article):
虽然浮点加法和乘法都是可交换的(a + b = b + a和a×b = b×a),但它们不一定是关联的。也就是说,(a + b)+ c不一定等于a +(b + c)。
以下是一个例子:
a: 0.825402526103613
b: 0.909231618470155
c: 0.654626872695343
(a*b)*c: 0.491285733573819
a*(b*c): 0.49128573357382
有些示例在结果变为string
时结果显示相同,但不同((a*b)*c != a*(b*c)
为true
,(a*b)*c - a*(b*c)
返回较小的值,不是0
)。
a: 0.613781429181705
b: 0.648859122604532
c: 0.795545351596337
(a*b)*c: 0.316832045751117
a*(b*c): 0.316832045751117
difference: 5.55111512312578E-17
答案 1 :(得分:2)
是int
是,不(不幸的选择)float
(由于精度有限)。
答案 2 :(得分:1)
在未经检查的上下文(默认值)中,溢出的存在与关联性和可交换性无关。
例如,取0x7fffffff + 0x80000000 + 0xffffffff
。无论你如何将它括起来,结果都是0xfffffffe
(又称-2)。但(0x7fffffff + 0x80000000) + 0xffffffff
不会溢出而0x7fffffff + (0x80000000 + 0xffffffff)
溢出两次。 (在无符号算术中,它们都会溢出一次)
总的来说也是如此。 C#中的整数a + (b + c) = (a + b) + c(如果unchecked
)和Java。在C和C ++中,只保证无符号整数,因为未定义有符号溢出。
答案 3 :(得分:0)
并行缩减:
floats.AsParallel().Sum();
......每次都可以得到不同的结果。这是因为每次都会对工作进行不同的分区,这会在添加数字时导致不同的舍入错误。
要最大限度地减少此问题,请确保使用double来完成缩减,而不是使用浮点数。如果您想要按位相同的结果,则不应使用TPL或PLINQ,并滚动自己的并行计算。
请注意,在C#中,fp的准确性不能像C ++那样严格保证。阅读Eric对this SO question.
的回答