5

在数学中,(实数的)加法是可交换的和关联的,即。对于所有数字 x、y 和 z

x + y = y + x(交换律)

x + (y + z) = (x + y) + z(关联性)

实数的乘法也是可交换的和结合的。但是对于 .NET 中的整数和浮点数来说,这是真的吗?欢迎反例。

编辑:背景是我们最近并行化了一个算法,现在它的结果在重复之间不再一致。我推测这可能是由于原子计算以不确定的顺序返回(并被合并)。在这种情况下,可以通过更智能(但更慢)的合并算法(在合并之前对结果进行排序)来修复不一致。我想知道它可以对 .NET 算术做出哪些假设。

4

4 回答 4

10

假设没有发生未经检查的溢出,.NET 的整数加法和乘法(int,long等)是可交换的和关联的,就像数学中的实数一样。由于精度的限制,浮点算术 (floatdouble) 是可交换的,但并不总是精确关联的。来自维基百科(文章中有一个例子)

虽然浮点加法和乘法都是可交换的(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

有一些示例,当转换为 a 时,结果看起来相同string,但不同((a*b)*c != a*(b*c)is true,并(a*b)*c - a*(b*c)返回一个小值, not 0)。

a: 0.613781429181705
b: 0.648859122604532
c: 0.795545351596337
(a*b)*c: 0.316832045751117
a*(b*c): 0.316832045751117
difference: 5.55111512312578E-17
于 2013-10-14T11:36:03.980 回答
2

是的int,不是(不幸的选择)float(因为精度有限)。

于 2013-10-14T11:31:27.193 回答
1

在未经检查的上下文(默认)中,溢出的存在与关联性和交换性无关。

例如,采取0x7fffffff + 0x80000000 + 0xffffffff. 结果是0xfffffffe(又名-2),无论你如何用括号括起来。但(0x7fffffff + 0x80000000) + 0xffffffff不会溢出而0x7fffffff + (0x80000000 + 0xffffffff)溢出两次。(在无符号算术中,它们都会溢出一次)

一般情况下也是如此。a + (b + c) = (a + b) + c在 C# (if unchecked) 和 Java 中的整数。在 C 和 C++ 中,这仅对无符号整数有保证,因为有符号溢出是未定义的。

于 2013-10-14T13:14:35.007 回答
0

在并行化约简中:

floats.AsParallel().Sum();

...您每次都可以期待不同的结果。这是因为每次对工作进行不同的分区,导致在相加时出现不同的舍入误差。

为了最大限度地减少这个问题,请确保使用双精度而不是浮点数进行归约。如果您想要按位相同的结果,则不应使用 TPL 或 PLINQ,而应使用自己的并行计算。

请注意,在 C# 中,fp 准确性并不能像在 C++ 中那样严格保证。阅读埃里克对 这个 SO 问题的回答。

于 2013-10-14T11:55:37.963 回答