C#和花车的恶作剧

C#和花车的恶作剧,c#,floating-point,rounding,C#,Floating Point,Rounding,在测试为什么我的程序不能按预期工作时,我尝试在即时窗口中键入似乎失败的计算 Math.Floor(1.0f) 1.0 - correct 然而: 200f * 0.005f 1.0 Math.Floor(200f * 0.005f) 0.0 - incorrect 此外: (float)(200f * 0.005f) 1.0 Math.Floor((float)(200f * 0.005f)) 0.0 - incorrect 可能正在发生浮动损失,0.99963≠ 例如,1.00127

在测试为什么我的程序不能按预期工作时,我尝试在即时窗口中键入似乎失败的计算

Math.Floor(1.0f)
1.0 - correct
然而:

200f * 0.005f
1.0

Math.Floor(200f * 0.005f)
0.0 - incorrect
此外:

(float)(200f * 0.005f)
1.0

Math.Floor((float)(200f * 0.005f))
0.0 - incorrect
可能正在发生浮动损失,0.99963≠ 例如,1.00127

我不介意存储精度较低的值,但要以无损的方式存储,例如,如果有一种数值类型可以像整数一样存储值,但如果可以执行的话,只能存储到小数点后三位

我认为可能有一种更好的方法来计算(n*0.005f)这种误差

编辑:

这是一个解决方案:

Math.Floor(200m * 0.005m)
此外,据我所知,如果我不介意将1/200更改为1/256,这将起作用:

Math.Floor(200f * 0.00390625f)
我正在使用的解决方案。这是我能在我的程序中得到的最接近的,并且似乎工作正常:

float x = ...;
UInt16 n = 200;
decimal d = 1m / n;
... = Math.Floor((decimal)x * d)

如果不能容忍任何浮点精度问题,请使用
十进制


最终,即使是
decimal
也存在精度问题(它允许28-29位有效数字)。如果您在它支持的范围内工作(-7.9 x 10^28到7.9 x 10^28)/(100^28)),则不太可能受到它们的影响。

如果您不能容忍任何浮点精度问题,请使用
十进制


最终,即使是
decimal
也存在精度问题(它允许28-29位有效数字)。如果您在它支持的范围内工作(-7.9 x 10^28到7.9 x 10^28)/(100^28)),您就不太可能受到它们的影响。

浮点数将数字表示为分母中具有二次幂的分数。也就是说,可以精确表示1/2、3/4或19/256。由于.005是1/200,200不是2的幂,因此
0.005f
得到的是最接近的分数,其底部的幂为2,可以放入32位浮点

小数表示分母为十次幂的分数。与浮动一样,当您试图表示不符合该模式的数字时,它们会引入错误<例如,code>1m/333m
将为您提供最接近1/333的数字,该数字的分母为10的幂,有效数字为29或更少。因为0.005是5/1000,这是10的幂,所以
0.005m
将给出精确的表示。你付出的代价是小数比浮点数大得多,也慢得多


金融计算中应始终使用小数,而不要使用浮点数。

浮点数表示分母为二次幂的分数。也就是说,可以精确表示1/2、3/4或19/256。由于.005是1/200,200不是2的幂,因此
0.005f
得到的是最接近的分数,其底部的幂为2,可以放入32位浮点

小数表示分母为十次幂的分数。与浮动一样,当您试图表示不符合该模式的数字时,它们会引入错误<例如,code>1m/333m将为您提供最接近1/333的数字,该数字的分母为10的幂,有效数字为29或更少。因为0.005是5/1000,这是10的幂,所以
0.005m
将给出精确的表示。你付出的代价是小数比浮点数大得多,也慢得多


在财务计算中应该始终使用小数,而不是浮点数。

问题是0.005f实际上是0.0049999888241291046142578125。。。因此小于0.005。这是最接近0.005的
float
值。当你把它乘以200,你得到的结果是小于1

如果您一直使用
decimal
,而不是从
float
转换,则在这种特定情况下应该可以。因此:


但是,不要假设这意味着
十进制
具有“无限精度”。它仍然是一种精度有限的浮点类型-它只是一种浮点小数类型,因此0.005是完全可表示的。

问题是0.005f实际上是0.004999988241291046142578125。。。因此小于0.005。这是最接近0.005的
float
值。当你把它乘以200,你得到的结果是小于1

如果您一直使用
decimal
,而不是从
float
转换,则在这种特定情况下应该可以。因此:


但是,不要假设这意味着
十进制
具有“无限精度”。它仍然是一种精度有限的浮点类型-它只是一种浮点小数类型,因此0.005完全可以表示。

dude此问题在stackoverflow中有许多重复项,请使用decimal类型。您确定您的问题与许多现有问题不匹配吗?您的“解决方案”仍然是错误的。您必须使用
m
后缀,而不是将浮点常量强制转换为十进制。dude此问题在stackoverflow中有许多重复项,请使用十进制类型。您确定您的问题与许多现有问题不匹配吗?您的“解决方案”仍然是错误的。您必须使用
m
后缀,而不是将浮点常量强制转换为十进制。我刚刚尝试了Math.Floor((decimal)(n*0.005f))并且它似乎有效。我不明白为什么,(n*0.005f)不应该给出一个不精确的答案,然后转换成更高精度的浮点?此外,数学。Floor创建了一个锐利的截断,为什么(看起来)额外的精度会有帮助?@alan2这里:如果你被限制在浮点的32位,那么计算出分母中有2次幂的分数最接近0.005。现在计算出分母中的二次幂最接近0.005的分数,如果限制为小数点的64位。为什么每种情况下最接近的分数都必须大于或小于0.005?当我发布回复评论时,我没有看到你的答案。我刚刚尝试了Math.Floor((十进制)(n*0.005f))并且似乎有效。我不明白为什么,是吗
decimal x = 0.005m;
decimal y = 200m;
decimal z = x * y;
Console.WriteLine(z == 1m); // True