Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/37.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 这是虫子吗?浮点操作被视为整数_C#_Asp.net - Fatal编程技术网

C# 这是虫子吗?浮点操作被视为整数

C# 这是虫子吗?浮点操作被视为整数,c#,asp.net,C#,Asp.net,此操作返回0: string value = “0.01”; float convertedValue = float.Parse(value); return (int)(convertedValue * 100.0f); 但此操作返回1: string value = “0.01”; float convertedValue = float.Parse(value) * 100.0f; return (int)(convertedValue); 因为convertedValue是一个浮点

此操作返回0:

string value = “0.01”;
float convertedValue = float.Parse(value);
return (int)(convertedValue * 100.0f);
但此操作返回1:

string value = “0.01”;
float convertedValue = float.Parse(value) * 100.0f;
return (int)(convertedValue);

因为convertedValue是一个浮点值,并且它位于括号*100f中,所以它不应该仍然被视为浮点操作吗?

请阅读floatingpoint的介绍。这是一个典型的浮点问题。二进制浮点不能准确地表示
0.01

0.01*100
约为1

如果它恰好被四舍五入到
0.999…
则得到
0
,如果它被四舍五入到
1.000…
则得到1。你得到的哪一个是未定义的

jit编译器不需要在每次遇到类似的表达式(甚至是不同上下文中的相同表达式)时以相同的方式进行取整。特别是,它可以随时使用更高的精度,但如果它认为这是个好主意,可以降级到32位浮点


一个有趣的点是显式转换为
float
(即使您已经有了
float
类型的表达式)。这迫使JITer在该点将精度降低到32位浮点。然而,精确的四舍五入仍然没有定义

因为舍入是未定义的,所以它可以在.net版本、调试/发布版本、调试器的存在(可能还有月亮的阶段:p)之间变化

浮点数(静态、数组元素和类字段)的存储位置大小固定。这个 支持的存储大小为float32和float64。其他地方(在求值堆栈上,作为参数,作为 返回类型,作为局部变量)浮点数使用内部浮点数表示 类型

当将其内部表示形式的范围和/或精度大于其标称类型的浮点值放入存储位置时,它将自动强制为存储位置的类型。这可能涉及 精度损失或创建超出范围的值(NaN、+无穷大或-无穷大)但是,如果从存储位置重新加载该值而不进行更改,则该值可能会保留在内部表示中以供将来使用 已修改。编译器有责任确保保留值在后续加载时仍然有效,同时考虑别名和其他执行线程的影响(请参阅内存模型(§12.6))。但是,在执行显式转换(conv.r4或conv.r8)之后,不允许有携带额外精度的自由,此时内部表示必须 在关联类型中完全可表示


您的具体问题可以通过使用
Decimal
来解决,但是
3*(1/3f)
的类似问题无法通过此解决,因为
Decimal
也不能精确表示三分之一。

在这一行:

(int)(convertedValue * 100.0f)
中间值实际上具有更高的精度,而不仅仅是一个浮点值。要获得与第二个相同的结果,您必须执行以下操作:

(int)((float)(convertedValue * 100.0f))
在IL水平上,差异如下所示:

    mul 
    conv.i4 
    mul 
    conv.r4 
    conv.i4 
与您的第二个版本相比:

    mul 
    stloc.3 
    ldloc.3 
    conv.i4 
请注意,第二个变量存储/恢复
float32
变量中的值,这迫使它具有
float
精度。(请注意,根据CodeInChaos的意见,规范不保证这一点。)

(为了完整性,显式强制转换如下所示:)


我知道这个问题,并且一直在处理它。 正如我们的朋友CodeInChaose所回答的那样,浮点将不会在内存中显示为它的值

但我想补充的是,你有一个不同结果的原因,不是因为JIT可以自由使用他想要的精度

原因是在您的第一个代码中,您转换了字符串并将其保存在内存中,因此在这种情况下,它不会保存为0.1,而某些方式会保存为0.0999966或类似此数字的内容


在第二个代码中进行转换,在将其保存到内存中之前,以及在将值分配到内存中之前,您执行了乘法运算,这样您就可以得到正确的结果,而不必冒浮点数JIT精度的风险。

两者之间的区别在于编译器优化浮点数的方式操作。让我解释一下

string value = "0.01";
float convertedValue = float.Parse(value);
return (int)(convertedValue * 100.0f);

在本例中,该值被解析为80位浮点数,用于计算机的内部浮点副本。然后将其转换为32位浮点,以存储在
convertedValue
变量中。这会使该值四舍五入到略小于0.01的数字。然后将其转换回80位浮点并乘以100,使舍入误差增加100倍。然后将其转换为32位整数。这会导致浮点被截断,因为它实际上略小于1,所以整数转换返回0

string value = "0.01";
float convertedValue = float.Parse(value) * 100.0f;
return (int)(convertedValue);
在本例中,该值再次解析为80位浮点数。然后将其乘以100,然后再转换为32位浮点。这意味着舍入误差非常小,当将其转换为32位浮点以存储在
convertedValue
中时,舍入误差正好为1。然后,当它被转换为int时,得到1


其主要思想是计算机使用高精度浮点进行计算,然后在变量中存储值时对其进行四舍五入。浮点赋值越多,舍入误差就越大。

您应该强调这不是一个bug。好吧,我理解浮点可能有舍入误差,但为什么第一个块的舍入与第二个块的舍入不同?@user1270287,因为它可以。jit编译器可以自由地使用更高的精度,但不需要这样做。有时有,有时没有。@NoonSilk:这个答案绝对没有错。否决一个非常好的答案,因为t