Float正在转换我的价值观

时间:2011-11-04 16:18:16

标签: c# .net floating-point

我有一个测试值的方法在字段允许的范围内。如果它在范围之外,则返回null,如果inside返回值。

internal float? ExtractMoneyInRangeAndPrecision(string fieldValue, string fieldName, float min, float max, int scale, int lineNumber)
    {          
        float returnValue;

        //Check whether valid float if
        if (float.TryParse(fieldValue, out returnValue))
        {
            //Check whether in range
            if (returnValue >= min && returnValue <= max)
            {
                int decPosition = 0;
                decPosition = fieldValue.IndexOf('.');

                if (
                    (decPosition == -1) ||
                    ((decPosition != -1) && (fieldValue.Substring(decPosition, fieldValue.Length - decPosition).Length -1 <= scale))
                    )
                {
                    return returnValue;
                }
            }
        }            

        return null;
    }

这是我的单元测试:

[TestMethod()]
    [DeploymentItem("ImporterEngine.dll")]
    public void ExtractMoneyInRangeAndPrecisionTest_OutsideRange()
    {
        MockSyntaxValidator target = new MockSyntaxValidator("", 0);
        string fieldValue = "1000000";
        string fieldName = "";
        float min = 1;
        float max = 999999.99f;
        int scale = 2;
        int lineNumber = 0;
        float? Int16RangeReturned;

        Int16RangeReturned = target.ExtractMoneyInRangeAndPrecision(fieldValue, fieldName, min, max, scale, lineNumber);

        Assert.IsNull(Int16RangeReturned);
    }

正如您所看到的,最大值是999999.99但是当该方法将其更改为1,000,000时

为什么会这样?

4 个答案:

答案 0 :(得分:3)

http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

简而言之,由于浮点数表示实数的方式,您为浮点数指定的数字并不总是您返回的数字。您指定的值将转换为最接近的值,该值可以用科学计数法表示,其大小由2的基数确定。

在999999.99的情况下,最接近的数字可以表示为具有相同数量的sig figs的浮点数是7.6293945 * 2 17 = 999999.99504,当舍入到相同的sig figs时1,000,000.00。这可能不是一个确切的例子,但这样的错误是使用浮点数所固有的。

在要求精确度达到精确度的情况下,请勿使用浮点类型。而是使用十进制类型,它将保留输入值的精度。

答案 1 :(得分:1)

浮点类型(在C#中定义)是近似值。为了精确,您应始终使用decimal

来自MSDN:

  

decimal关键字表示128位数据类型。 与浮点类型相比,十进制类型具有更高的精度和更小的范围,这使其适用于财务和货币计算。十进制类型的近似范围和精度如下表所示

http://msdn.microsoft.com/en-us/library/364x0z75.aspx

在C#中有什么资格作为浮点类型似乎存在争议。虽然decimal通过实际定义确实符合浮点类型,但根据MSDN规范,它没有被定义为浮点类型。

http://msdn.microsoft.com/en-us/library/9ahet949.aspx

答案 2 :(得分:1)

并非每个数字字符串都可以转换为浮点数。没有检查,我会说999999.99就是这样一个数字。小数将解决这个问题。

答案 3 :(得分:1)

float类型没有足够的精度来执行您想要执行的操作。我建议使用decimal类型。浮点数最多可以精确到7位十进制数,你在这里使用8位数。十进制最多可以有28位数,这对于任何数量来说都足够了。此外,与float不同,编译器使用的值和您编写的值将始终相同。

这是一个很长的解释:

浮点数(单精度浮点数)存储为2的幂的整数倍,其中整数在一定范围内(2^232^24之间)。

当您在代码中写入十进制数时,编译器会将此解释为此表单中与您编写的数字最接近的数字。有时匹配是精确的(99999.75)。在其他情况下,您的数字需要四舍五入到最接近的浮点数。这就是这里发生的事情:

99999.99 = 2^19 * 1.907348613739013671875
         = 2^19 * 2^-23 * (2^23 * 1.907348613739013671875)
         = 2^-4 * 15999999.84

15999999.84最接近的整数是16000000,因此舍入值为

(float)99999.99 = 2^-4 * 16000000 
                = 1000000

decimal类型的一大优势是表示为96位整数乘以10的幂,因此可以精确表示最多28位的十进制数,而不进行任何舍入。你所看到的就是你得到的。

decimal的最大缺点是速度明显变慢,但在这种情况下,你将字符串转换为数字,这不是一个因素。