相关疑难解决方法(0)

浮点数学在C#中是否一致?是真的吗?

不,这不是另一个"为什么是(1/3.0)*3!= 1"的问题.

我最近一直在读关于漂浮点的事情; 具体而言,相同的计算如何在不同的体系结构或优化设置上给出不同的结果.

对于存储重放的视频游戏来说,这是一个问题,或者是对等网络(而不是服务器 - 客户端),它依赖于每次运行程序时产生完全相同结果的所有客户端 - 一个小的差异浮点计算可能导致不同机器(甚至是同一台机器上)的游戏状态截然不同!

甚至在"跟随" IEEE-754的处理器中也会发生这种情况,这主要是因为某些处理器(即x86)使用双倍扩展精度.也就是说,它们使用80位寄存器进行所有计算,然后截断为64位或32位,导致与使用64位或32位进行计算的机器不同的舍​​入结果.

我在网上看到过这个问题的几种解决方案,但都是针对C++,而不是C#:

  • double使用_controlfp_s(Windows),_FPU_SETCW(Linux?)或fpsetprec(BSD)禁用双扩展精度模式(以便所有计算使用IEEE-754 64位).
  • 始终使用相同的优化设置运行相同的编译器,并要求所有用户具有相同的CPU架构(无跨平台播放).因为我的"编译器"实际上是JIT,它可能在每次运行程序时都有不同的优化,我认为这是不可能的.
  • 使用定点算术,避免floatdouble完全.decimal可以用于此目的,但会慢得多,并且没有任何System.Math库函数支持它.

那么,这在C#中是否也是一个问题? 如果我只打算支持Windows(而不是Mono)怎么办?

如果是,有没有办法强制我的程序以正常的双精度运行?

如果没有,是否有任何库可以帮助保持浮点计算的一致性?

.net c# floating-point precision ieee-754

152
推荐指数
6
解决办法
1万
查看次数

(.1f + .2f ==.3f)!=(.1f + .2f).Equals(.3f)为什么?

我的问题不是浮动精度.这是为什么Equals()不同于==.

我明白为什么.1f + .2f == .3ffalse(同时.1m + .2m == .3mtrue).
我得到的==是参考,.Equals()是价值比较.(编辑:我知道还有更多.)

但是,为什么(.1f + .2f).Equals(.3f) true,而(.1d+.2d).Equals(.3d)仍然是false

 .1f + .2f == .3f;              // false
(.1f + .2f).Equals(.3f);        // true
(.1d + .2d).Equals(.3d);        // false
Run Code Online (Sandbox Code Playgroud)

c# equality floating-accuracy

67
推荐指数
3
解决办法
5938
查看次数

将结果转换为float方法返回float更改结果

为什么此代码False在.NET 4中打印?似乎一些意外的行为是由显式演员引起的.

我想要一个超越"浮动点不准确"或"不要那样做"的答案.

float a(float x, float y)
{
  return ( x * y );
}

float b(float x, float y)
{
  return (float)( x * y );
}

void Main()
{
  Console.WriteLine( a( 10f, 1f/10f ) == b( 10f, 1f/10f ) );
}
Run Code Online (Sandbox Code Playgroud)

PS:此代码来自单元测试,而不是发布代码.代码是故意以这种方式编写的.我怀疑它最终会失败,但我想知道确切的时间和原因.答案证明了这种技术的有效性,因为它提供的理解超出了对浮点确定性的通常理解.这就是用这种方式编写代码的重点; 刻意探索.

PPS:单元测试在.NET 3.5中传递,但现在在升级到.NET 4后失败了.

.net c# floating-point casting .net-4.0

53
推荐指数
1
解决办法
5133
查看次数

在.NET中强制浮点是确定性的吗?

我一直在阅读很多关于.NET中浮点确定性的内容,即确保具有相同输入的相同代码将在不同的机器上提供相同的结果.由于.NET缺少Java的fpstrict和MSVC的fp:strict等选项,因此似乎一致认为使用纯托管代码无法绕过这个问题.C#游戏AI Wars已经决定使用定点数学,但这是一个麻烦的解决方案.

主要问题似乎是CLR允许中间结果存在于FPU寄存器中,这些寄存器具有比类型的原始精度更高的精度,从而导致不可预测的更高精度结果.CLR工程师David Notario撰写MSDN文章解释了以下内容:

请注意,对于当前规范,它仍然是提供"可预测性"的语言选择.在每次FP操作之后,该语言可以插入conv.r4或conv.r8指令以获得"可预测的"行为. 显然,这非常昂贵,不同的语言有不同的妥协.例如,C#什么都不做,如果你想缩小,你必须手动插入(浮点)和(双)强制转换.

这表明,只需为每个表达式和计算浮点数的子表达式插入显式强制转换,就可以实现浮点确定性.有人可能会在float周围编写一个包装器类型来自动执行此任务.这将是一个简单而理想的解决方案!

然而,其他评论表明它并非如此简单.Eric Lippert最近表示(强调我的):

在某些版本的运行时中,显式转换为float会产生与不这样做不同的结果.当你明确地转换为float时,C#编译器会给运行时提供一个提示,说"如果碰巧使用这个优化,就把这个东西从超高精度模式中取出".

这对运行时的"提示"是什么?C#规范是否规定显式转换为float会导致在IL中插入conv.r4?CLR规范是否规定conv.r4指令会使值缩小到其原始大小?只有当这两者都成立时,我们才能依靠显式转换来提供浮点"可预测性",正如David Notario所解释的那样.

最后,即使我们确实能够将所有中间结果强制转换为类型的原生大小,这是否足以保证跨机器的可重复性,还是有其他因素如FPU/SSE运行时设置?

.net c# floating-point ieee-754

47
推荐指数
2
解决办法
4835
查看次数

C# - 32位和64位的数学运算结果不一致

请考虑以下代码:

double v1 = double.MaxValue;
double r = Math.Sqrt(v1 * v1);
Run Code Online (Sandbox Code Playgroud)

r = double.MaxValue在32位机器上r = 64位机器上的无穷大

我们在32位机器上开发,因此在客户通知之前不会发现问题.为什么会发生这种不一致?如何防止这种情况发生?

c#

11
推荐指数
1
解决办法
2670
查看次数

为什么这个浮点计算在不同的机器上给出不同的结果?

我有一个简单的例程,它从浮点值计算宽高比.因此,对于值1.77777779,例程返回字符串"16:9".我在我的机器上测试了它,它工作正常.

例程如下:

    public string AspectRatioAsString(float f)
    {
        bool carryon = true;
        int index = 0;
        double roundedUpValue = 0;
        while (carryon)
        {
            index++;
            float upper = index * f;

            roundedUpValue = Math.Ceiling(upper);

            if (roundedUpValue - upper <= (double)0.1 || index > 20)
            {
                carryon = false;
            }
        }

        return roundedUpValue + ":" + index;
    }
Run Code Online (Sandbox Code Playgroud)

现在在另一台机器上,我得到了完全不同的结果.所以在我的机器上,1.77777779给出"16:9",但在另一台机器上我得到"38:21".

c#

9
推荐指数
3
解决办法
8730
查看次数

C#XNA Visual Studio:"发布"和"调试"模式之间的区别?

我正在研究一个关于碰撞检测的演示.(这里详细说明一些代码.)在调试模式下,它工作正常.在释放模式下,它更快,但碰撞检测真的搞砸了.物体没有任何反弹,或者似乎被重力奇怪地轻微影响.有些物体爆炸,好像它们与特殊爆炸物体相撞,即使这些物体都不存在.

那么...... Visual Studio在Release和Debug模式之间改变了什么导致了这个问题呢?(我正在使用VS Pro 2008.)

神秘的是,发布模式一直在努力进行大量的开发.它刚刚停止了.

c# xna visual-studio-2008

7
推荐指数
2
解决办法
3059
查看次数

浮点文字与浮点变量的奇怪编译器行为

我注意到C#编译器浮动舍入/截断有趣的行为.也就是说,当浮点字面值超出保证的可表示范围(7个十进制数字)时,则a)显式地将float结果转换为float(语义上不必要的操作)和b)将中间计算结果存储在局部变量中都会改变输出.一个例子:

using System;

class Program
{
    static void Main()
    {
        float f = 2.0499999f;
        var a = f * 100f;
        var b = (int) (f * 100f);
        var c = (int) (float) (f * 100f);
        var d = (int) a;
        var e = (int) (float) a;
        Console.WriteLine(a);
        Console.WriteLine(b);
        Console.WriteLine(c);
        Console.WriteLine(d);
        Console.WriteLine(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出是:

205
204
205
205
205
Run Code Online (Sandbox Code Playgroud)

在我的计算机上的JITted调试版本中,b的计算方法如下:

          var b = (int) (f * 100f);
0000005a  fld         dword ptr [ebp-3Ch] 
0000005d  fmul        dword ptr ds:[035E1648h] 
00000063  fstp        qword …
Run Code Online (Sandbox Code Playgroud)

c# compiler-construction floating-point

7
推荐指数
1
解决办法
864
查看次数

为什么在由parantheses分隔和由语句分隔时,C#中的浮点精度会有所不同?

我知道浮点精度在常规情况下是如何工作的,但我偶然发现了我的C#代码中的奇怪情况.

为什么result1和result2在这里不是完全相同的浮点值?


const float A;   // Arbitrary value
const float B;   // Arbitrary value

float result1 = (A*B)*dt;

float result2 = (A*B); 
result2 *= dt;
Run Code Online (Sandbox Code Playgroud)

这个页面我认为浮动算术是左关联的,这意味着值以从左到右的方式进行评估和计算.

完整的源代码涉及XNA的Quaternions.我不认为我的常量是什么以及VectorHelper.AddPitchRollYaw()的作用.如果我以相同的方式计算三角形俯仰/滚转/偏航角度,测试通过就好了,但是由于代码低于它不通过:


X
  Expected: 0.275153548f
  But was:  0.275153786f
Run Code Online (Sandbox Code Playgroud)

[TestFixture]
    internal class QuaternionPrecisionTest
    {
        [Test]
        public void Test()
        {
            JoystickInput input;
            input.Pitch = 0.312312432f;
            input.Roll = 0.512312432f;
            input.Yaw = 0.912312432f;
            const float dt = 0.017001f;

            float pitchRate = input.Pitch * PhysicsConstants.MaxPitchRate;
            float rollRate = input.Roll * PhysicsConstants.MaxRollRate;
            float yawRate = input.Yaw * PhysicsConstants.MaxYawRate;

            Quaternion …
Run Code Online (Sandbox Code Playgroud)

c# floating-point precision

5
推荐指数
2
解决办法
1508
查看次数

不相关的代码会更改计算结果

我们有一些代码会在某些机器上产生意外结果.我把它缩小到一个简单的例子.在下面的linqpad片段中,方法GetValGetVal2实现基本相同,尽管前者还包括对NaN的检查.但是,每个返回的结果都不同(至少在我的机器上).

void Main()
{
    var x = Double.MinValue;
    var y = Double.MaxValue;
    var diff = y/10 - x/10;

    Console.WriteLine(GetVal(x,6,diff));
    Console.WriteLine(GetVal2(x,6,diff));
}

public static double GetVal(double start, int numSteps, double step)
{
    var res = start + numSteps * step;
    if (res == Double.NaN)
        throw new InvalidOperationException();
    return res;
}

public static double GetVal2(double start, int numSteps, double step)
{
    return start + numSteps * step;
}
Run Code Online (Sandbox Code Playgroud)

结果

3.59538626972463E+307
Infinity
Run Code Online (Sandbox Code Playgroud)

为什么会发生这种情况,是否有一种避免它的简单方法?与寄存器有关?

c# floating-point cpu-registers

5
推荐指数
1
解决办法
110
查看次数

C#中的舍入错误:不同PC上的结果不同

我在C#中有一个函数返回以下内容:

...
float amount = smallestPercentage * (float)quantity;
return (int)amount;
Run Code Online (Sandbox Code Playgroud)

现在我知道我假设使用Convert.Int32(金额)而不是类型转换为int,这解决了问题.但我的问题真的是......

在家中开发我的程序(Windows Vista)时,我会得到1的返回值,但是当将程序部署到另一个环境(Windows XP)时,我会得到一个返回值0.

我想知道这是否与Windows版本,.NET版本甚至CPU处​​理器有关?

谢谢.

大卫

c# rounding

0
推荐指数
1
解决办法
1377
查看次数