相关疑难解决方法(0)

.NET中的decimal,float和double之间的区别?

是什么区别decimal,floatdouble在.NET?

什么时候会有人使用其中一种?

.net floating-point double decimal

2015
推荐指数
15
解决办法
88万
查看次数

浮点数学在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万
查看次数

在C#中将float转换为int时的奇怪行为

我有以下简单的代码:

int speed1 = (int)(6.2f * 10);
float tmp = 6.2f * 10;
int speed2 = (int)tmp;
Run Code Online (Sandbox Code Playgroud)

speed1和speed2应该具有相同的值,但事实上,我有:

speed1 = 61
speed2 = 62
Run Code Online (Sandbox Code Playgroud)

我知道我应该使用Math.Round而不是cast,但我想了解为什么值不同.

我查看了生成的字节码,但除了存储和加载外,操作码是相同的.

我也在java中尝试了相同的代码,我正确地获得了62和62.

有人可以解释一下吗?

编辑: 在实际代码中,它不是直接6.2f*10而是函数调用*常量.我有以下字节码:

速度1:

IL_01b3:  ldloc.s    V_8
IL_01b5:  callvirt   instance float32 myPackage.MyClass::getSpeed()
IL_01ba:  ldc.r4     10.
IL_01bf:  mul
IL_01c0:  conv.i4
IL_01c1:  stloc.s    V_9
Run Code Online (Sandbox Code Playgroud)

速度2:

IL_01c3:  ldloc.s    V_8
IL_01c5:  callvirt   instance float32 myPackage.MyClass::getSpeed()
IL_01ca:  ldc.r4     10.
IL_01cf:  mul
IL_01d0:  stloc.s    V_10
IL_01d2:  ldloc.s    V_10
IL_01d4:  conv.i4
IL_01d5:  stloc.s    V_11
Run Code Online (Sandbox Code Playgroud)

我们可以看到操作数是浮点数,唯一的区别是stloc/ldloc

至于虚拟机,我尝试使用Mono/Win7,Mono/MacOS和.NET/Windows,结果相同

c# floating-point int expression casting

127
推荐指数
3
解决办法
2万
查看次数

在.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
查看次数

浮点运算是否稳定?

我知道浮点数具有精度,精度后的数字不可靠.

但是如果用于计算数字的等式是相同的呢?我可以假设结果也一样吗?

例如,我们有两个浮点数xy.我们可以假设x/y机器1的结果与机器2的结果完全相同吗?IE ==比较将返回true

c# floating-point

37
推荐指数
4
解决办法
3157
查看次数

使用System.Double的Assert.AreEqual()变得非常混乱

描述

这不是一个现实世界的例子!请不要建议使用decimal或其他东西.

我只是问这个,因为我真的想知道为什么会这样.

我最近再次与Jon Skeet一起看到了令人敬畏的Tekpub网络广播C#4.0.

在第7- 小数和浮点数它真的很奇怪,甚至我们的 Chuck Norris of Programming(又名Jon Skeet)对我的问题没有真正的答案.只有一个可能.

问题:为什么MyTestMethod()失败并MyTestMethod2()通过?

例1

[Test]
public void MyTestMethod()
{
    double d = 0.1d;
    d += 0.1d;
    d += 0.1d;
    d += 0.1d;
    d += 0.1d;
    d += 0.1d;
    d += 0.1d;
    d += 0.1d;
    d += 0.1d;
    d += 0.1d;

    Console.WriteLine("d = " + d);
    Assert.AreEqual(d, 1.0d);
}
Run Code Online (Sandbox Code Playgroud) 这导致了

d = 1

预期:0.99999999999999989d但是:1.0d …

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

19
推荐指数
4
解决办法
2万
查看次数

为什么在变量中存储值会改变相等比较的结果?

输出以下代码:

var a = 0.1;
var count = 1;

while (a > 0)
{
    if (count == 323)
    {
        var isZeroA = (a * 0.1) == 0;
        var b = a * 0.1;
        var isZeroB = b == 0;

        Console.WriteLine("IsZeroA: {0}, IsZeroB: {1}", isZeroA, isZeroB);
    }

    a *= 0.1;
    ++count;
}
Run Code Online (Sandbox Code Playgroud)


IsZeroA:错,IsZeroB:是的

奇怪的是,当我if (count == 323)在调试并(a * 0.1) == 0在Visual Studio Watch窗口中放置表达式之后放置断点时,它会报告表达式true.

有谁知道为什么表达式a * 0.1不为零,但是当分配给变量时b,则b为零?

.net c# clr

9
推荐指数
1
解决办法
91
查看次数

如果Float首次保存到变量,浮动乘法的铸造结果会产生不同的结果?

这里的代码是直截了当的,但我不明白结果:

float percent = 0.69f;
int firstInt = (int)(percent*100f);

float tempFloat = percent*100f;
int secondInt = (int)tempFloat;

Debug.Log(firstInt + " " + secondInt);
Run Code Online (Sandbox Code Playgroud)

为什么是firstInt68但是secondInt69?

c# precision casting floating-accuracy

8
推荐指数
1
解决办法
400
查看次数

在64位计算机上运行x86编译的代码时,单精度算术被破坏

当您阅读MSDN时System.Single:

Single 符合IEC 60559:1989(IEEE 754)二进制浮点运算标准.

和C#语言规范:

这些floatdouble类型使用32位单精度和64位双精度IEEE 754格式表示[...]

然后:

根据IEEE 754算法的规则计算产品.

您很容易得到float类型及其乘法符合IEEE 754 的印象.

IEEE 754的一部分是多重定义.我的意思是,当你有两个float实例时,存在一个且只有一个float是他们的"正确"产品.不允许产品依赖于计算系统的某些"状态"或"设置".

现在,考虑以下简单程序:

using System;

static class Program
{
  static void Main()
  {
    Console.WriteLine("Environment");
    Console.WriteLine(Environment.Is64BitOperatingSystem);
    Console.WriteLine(Environment.Is64BitProcess);
    bool isDebug = false;
#if DEBUG
    isDebug = true;
#endif
    Console.WriteLine(isDebug);
    Console.WriteLine();

    float a, b, product, whole;

    Console.WriteLine("case .58");
    a = 0.58f;
    b = 100f;
    product = a * b;
    whole = 58f;
    Console.WriteLine(whole == …
Run Code Online (Sandbox Code Playgroud)

c# floating-point clr 64-bit single-precision

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

为什么0.9重复总是等于1

在数学上,0.9重复可以显示为等于1.然而,这个问题不是关于无穷大,收敛或这背后的数学.

上述假设可以用C#中的双精度表示,具体如下.

var oneOverNine = 1d / 9d;
var resultTimesNine = oneOverNine * 9d;
Run Code Online (Sandbox Code Playgroud)

使用上面的代码,(resultTimesNine == 1d)计算结果为true.

当使用小数代替时,评估结果为false,但是,我的问题不是关于double和decimal的不同精度.

由于没有类型具有无限精度,如何以及为什么双重维持这样的相等而小数不是?关于oneOverNine变量存储在内存中的方式,上面代码的"行之间"发生了什么?

.net c# math

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

VS2012和VS2015之间的浮动/双转换差异

我有以下代码:

 float a = 0.02f * 28f;
 double b = (double)a;
 double c = (double)(0.02f * 28f);
 Console.WriteLine(String.Format("  {0:F20}",  b));
 Console.WriteLine(String.Format("  {0:F20}", c));
Run Code Online (Sandbox Code Playgroud)

然而,无论是从VS2012还是VS2015(均具有"标准"设置)编译,它都会返回不同的结果

在VS2012

0,56000000238418600000
0,55999998748302500000
Run Code Online (Sandbox Code Playgroud)

在VS2015中:

0,56000000238418600000
0,56000000238418600000
Run Code Online (Sandbox Code Playgroud)

VS2012解散:

           float a = 0.02f * 28f;
0000003a  mov         dword ptr [ebp-40h],3F0F5C29h 
            double b = (double)a;
00000041  fld         dword ptr [ebp-40h] 
00000044  fstp        qword ptr [ebp-48h] 
            double c = (double)(0.02f * 28f);
00000047  fld         qword ptr ds:[001D34D0h] 
0000004d  fstp        qword ptr [ebp-50h] 
Run Code Online (Sandbox Code Playgroud)

VS2015解散:

            float a = 0.02f * 28f;
001E2DE2 …
Run Code Online (Sandbox Code Playgroud)

c# visual-studio visual-studio-2012 visual-studio-2015

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

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

我们有一些代码会在某些机器上产生意外结果.我把它缩小到一个简单的例子.在下面的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
查看次数