我可以说使用double(或float)代替以下三个优点decimal:
但这些优势似乎只适用于计算密集型操作,例如建模软件中的操作.当然,在需要精确度时,不应使用双精度数,例如财务计算.那么有没有任何实际的理由选择double(或float)而不是decimal"正常"的应用程序?
编辑补充:感谢所有伟大的回应,我向他们学习.
还有一个问题:一些人认为双打可以更精确地代表实数.宣布时,我认为他们通常也会更准确地代表他们.但是,当执行浮点运算时,准确度是否会降低(有时是显着的)是真实的说法吗?
我有以下简单的代码:
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,结果相同
为什么此代码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中浮点确定性的内容,即确保具有相同输入的相同代码将在不同的机器上提供相同的结果.由于.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运行时设置?
我知道浮点数具有精度,精度后的数字不可靠.
但是如果用于计算数字的等式是相同的呢?我可以假设结果也一样吗?
例如,我们有两个浮点数x和y.我们可以假设x/y机器1的结果与机器2的结果完全相同吗?IE ==比较将返回true
我发现跑步了
Math.Log10(double.Epsilon)
Run Code Online (Sandbox Code Playgroud)
将-324在机器A上返回,但将-Infinity在机器B上返回.
他们最初的表现方式与回归相同-324.
两台机器都使用相同的操作系统(WinXP SP3)和.NET版本(3.5 SP1).机器B上可能有Windows更新,但是不知道发生了什么变化.
什么可以解释行为上的差异?
评论中讨论的更多细节:
Math.Log10(double.Epsilon)在机器B上打印一个简单的控制台应用程序-324,而不是-Infinity0x9001F(读取_controlfp()).更新:最后一点(FPU控制字)不再成立:使用较新版本的_controlfp()显示不同的控制字,这解释了不一致的行为.(有关详细信息,请参阅下面的rsbarro答案.)
我知道浮点计算存在准确性问题,并且有很多问题可以解释原因.我的问题是,如果我运行两次相同的计算,我是否可以始终依赖它来产生相同的结果?哪些因素可能会影响这个?
我有一个简单的物理模拟,并希望记录会话,以便他们可以重播.如果可以依赖计算,那么我只需要记录初始状态加上任何用户输入,我应该始终能够完全重现最终状态.如果计算不准确,那么在开始时错误可能会在模拟结束时产生巨大影响.
我目前在Silverlight工作,但有兴趣知道这个问题是否可以回答一般.
更新: 初始答案表明是,但显然这并不完全清楚,如所选答案的评论中所述.看起来我将不得不做一些测试,看看会发生什么.
假设您必须计算域在0.01到360.01之间的正弦(余弦或正切 - 无论如何).(使用C#)
什么会更高效?
我会反驳说,鉴于域名,选项2会快得多.在域精度(0.0000n)的什么时刻,计算的性能超过了查找.
相同的数学运算可以在不同的架构或浏览器中返回不同的结果吗?
在添加可空浮动时,我注意到了一些非常奇怪的东西.请使用以下代码:
float? a = 2.1f;
float? b = 3.8f;
float? c = 0.2f;
float? result =
(a == null ? 0 : a)
+ (b == null ? 0 : b)
+ (c == null ? 0 : c);
float? result2 =
(a == null ? 0 : a.Value)
+ (b == null ? 0 : b.Value)
+ (c == null ? 0 : c.Value);
Run Code Online (Sandbox Code Playgroud)
result为6.099999而result2为6.1.我很幸运,有过一次,因为如果我改变值这个跌跌撞撞a,b和c行为通常显示正确.其他算术运算符或其他可空值类型也可能发生这种情况,但这是我能够重现的情况.我不明白的是,为什么隐式转换为float …
c# ×9
math ×4
.net ×3
casting ×2
.net-4.0 ×1
decimal ×1
double ×1
epsilon ×1
expression ×1
ieee-754 ×1
int ×1
javascript ×1
performance ×1
silverlight ×1
types ×1