Why Convert.ToInt32(1.0/0.00004) != (Int32)(1.0/0.

2019-07-17 09:51发布

问题:

Why this code http://ideone.com/YRcICG

void Main()
{   
    double a = 0.00004;
    Int32 castToInt = (Int32)(1.0/a);
    Int32 convertToInt = Convert.ToInt32(1.0/a);

    Console.WriteLine("{0} {1:F9} {2:F9}", castToInt == convertToInt, castToInt, convertToInt);

    Console.WriteLine((((int)(1.0/(1.0/25000))) == 24999));
}

results in

False 24999,000000000 25000,000000000
True

in context of CLR/C# implementation

回答1:

The trick lies in the way the double is represented so (1.0/a) will be represented in the following way:

(1.0/a) = 24999.99999999999636202119290828704833984375.

When you use cast you get only the first part of this number, while the convert Method works in a different way, here is the code for the Convert method:

public static int ToInt32(double value)
{
    if (value >= 0.0)
    {
        if (value < 2147483647.5)
        {
            int num = (int)value;
            double num2 = value - (double)num;
            if (num2 > 0.5 || (num2 == 0.5 && (num & 1) != 0))
            {
                num++;
            }
            return num;
        }
    }
    else
    {
        if (value >= -2147483648.5)
        {
            int num3 = (int)value;
            double num4 = value - (double)num3;
            if (num4 < -0.5 || (num4 == -0.5 && (num3 & 1) != 0))
            {
                num3--;
            }
            return num3;
        }
    }
    throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
}

As you can see there is an if statement that checks the difference between casted value and original double, in your example it is:

int num = (int)value;
double num2 = value - (double)num;

24999.99999999999636202119290828704833984375 - 24999 > 0.5

, hence you get the increment.



回答2:

In your calculation, the answer to 1.0/0.00004 is getting converted to a value very slightly smaller than 2500, because floating-point numbers can't precisely represent all possible values. Given that,

Why do the two integers have different values?

Casting a double to an int truncates the value, so everything after the decimal point is discarded.

Convert.ToInt32 on a double rounds to the nearest integer.

Why are floating point numbers imprecise?

See the excellent article linked in another answer: What Every Computer Scientist Should Know About Floating-Point Arithmetic

How can I represent values precisely?

You could use a decimal type rather than double. There are pros and cons to doing this, see decimal vs double! - Which one should I use and when?



回答3:

Cast trunk the floating number

(Int32)41.548 == 41

Convert round the number (feature?)

Convert.ToInt32(41.548) == 42



回答4:

Floating point math is not exact. Simple values like 0.2 cannot be precisely represented using binary floating point numbers, and the limited precision of floating point numbers means that slight changes in the order of operations can change the result. A must read:

What Every Computer Scientist Should Know About Floating-Point Arithmetic

The IEEE standard divides exceptions into 5 classes: overflow, underflow, division by zero, invalid operation and inexact. There is a separate status flag for each class of exception. The meaning of the first three exceptions is self-evident. Invalid operation covers the situations listed in TABLE D-3, and any comparison that involves a NaN.