C# get digits from float variable

前端 未结 12 1634
南笙
南笙 2020-12-06 16:26

I have a float variable and would like to get only the part after the comma, so if I have 3.14. I would like to get 14 as an integer. How can I do that?

相关标签:
12条回答
  • 2020-12-06 17:04

    This will result in some odd unpredictable values.

    Floating point numbers are not stored as a decimal - the exponent part is a power of 2, not 10.

    This means that some numbers (for instance 1.1) can't be accurately expressed as a float (1.1 ends up something like 1.099999999998)

    The problem is that for some numbers the starting number may not be one of these while the decimal part on its own might be.

    So your number is x.y

    You get the integer part x

    You do x.y - x to get 0.y

    Sometimes x.y can be expressed as a float and 0.y can't, so rather than get y you'll get some big value with lots of 0s or 9s in it.

    @David's 'cheating' way is actually the best way - least prone to this issue anyway.

    However I'd look at why you need to do this - floats are great for very fast maths, but a bit rubbish for accuracy. If accuracy is important use a decimal type instead - that type guarantees that the precise value is stored, but at the cost of slower maths.

    0 讨论(0)
  • 2020-12-06 17:06

    Try

    float n = 3.14f;
    int fractionalPart = new System.Version(n.ToString()).Minor;
    

    David's "cheating version" answer doesn't seem to be very popular at the moment, but after looking into this for the better part of the day, I found the System.Version class. It has a constructor which takes a string. Using Reflector, I saw that it works by splitting the string into an array. I ran a test getting the fractional part of the arbitrary number 1234567891.1234567891m. With 1,000,000 iterations, it was 50% faster than the other answer I posted in spite of the fact that I first had to convert the decimal number to a string for the sake of the Version constructor. So David is getting a bad break when using a string conversion concept seems to be a bright way to go. Microsoft did.

    0 讨论(0)
  • 2020-12-06 17:07

    Actually all solutions until now are wrong as they don't consider that using Math.Floor() will do the wrong thing if the value is negative (e.g. Math.Floor(-2.8) -> -3)

    double number = -1234.56789;
    decimal numberM = Convert.ToDecimal(number);
    decimal fraction = Math.Abs(numberM - Math.Truncate(numberM));
    int mantissa = Convert.ToInt32((double)fraction * Math.Pow(10, fraction.ToString().Length - 2));
    
    0 讨论(0)
  • 2020-12-06 17:09

    Here's the "noncheating" answer:

    double n = 3.14;
    const double precision = 0.000001;
    
    // we don't handle negative numbers very well
    if (n < 0)
        n = 0 - n;
    
    // remove the integer part of n
    n -= Math.Floor(n);
    int result = 0;
    while (n > precision)
    {
        // move 1/10th digit of n into 1's place
        n *= 10;
        // get that digit
        int digit = (int)Math.Floor(n);
        // shift result left and add digit to it
        result = result * 10 + digit;
        // remove 1's digit from n
        n -= digit;
    }
    
    // answer is in result;
    

    We use precision instead of 0 to make up for the fact that floating point numbers don't work very well with decimal numbers. You can adjust it to suit your application. This is why I think the "cheating" string way is actually better.

    0 讨论(0)
  • 2020-12-06 17:10

    I saw a and fast way to convert floats/doubles to integers representative of their digits using a bitmask and the GetBits method... It only works if the results fit into a 32 bit integer, but it's still really slick... I can't take credit for it, but have a look:

    http://stsdb.com/showthread.php?t=58&p=285&viewfull=1#post285

    0 讨论(0)
  • 2020-12-06 17:11

    The cheating way to do it is:

        private Int32 FractionalPart(double n)
        {
            string s = n.ToString("#.#########", System.Globalization.CultureInfo.InvariantCulture);
            return Int32.Parse(s.Substring(s.IndexOf(".") + 1));
        }
    

    edit2: OK OK OK OK. Here is the most paranoid never fail version I can come up with. This will return the first 9 digits (or less, if there aren't that many) of the decimal portion of the floating point number. This is guaranteed to not overflow an Int32. We use the invariant culture so we know that we can use a period as the decimal separator.

    0 讨论(0)
提交回复
热议问题