views:

2536

answers:

8

Hi

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?

+9  A: 

You can subtract the integer portion from the value itself to retrieve the fractional part.

float x = 3.14
float fractionalPortion = x - Math.Floor(x);

You can then multiply it to get the fractional part represented as an integer at whatever precision you'd like.

Mapping the fractional portion to an integer has some challenges - many floating point numbers cannot be represented as a base-10 integer, and thus may require more digits to represent than an integer can support.

Also, what of the case of numbers like 3.1 and 3.01? Mapping directly to an integer would both result in 1.

Michael
Nice, that'll do it.
Aistina
That doesn't give him the fractional part as integer. That gives him a float with 0.14 ;)
jitter
Wont do it, since karstenkousgaard wants 14 as answer, not 0,14
Henri
Updated answer to include the required multiplication.
Michael
The last sentence in his answer says to multiply it, so this answer is valid. A sample would be nice.
bendewey
Of course you then need to multiply it by the appropriate power of 10 to get the number of decimal places you want.
Dolphin
Yes, but how do you determine the appropriate power of 10? That is the hard part of this question.
David
Especially when the number is pi :)
RedFilter
And may not be possible - an arbitrary base-2 floating point number cannot be mapped directly to a base ten integer.
Michael
You can't multiply by a power of 10 greater than the number of digits... so this omits the tough part of the question.
Will Eddins
A: 
float x = 3.14
int fractionalPortionAsInt = (int) (100 * (x - Math.Floor(x)));
jitter
For 3.14 that returns 10 * (3.14 - 0.14) = 30
David
doh. 10*(3.14 - 3) = 1.4 = (int)1 is what I meant.
David
umm should be 100. thx
jitter
@jitter: That means it only works for 2 digits, instead of any general floating point.
Will Eddins
yup. that's right
jitter
you have to specify a number of digits. 3.14 isn't exactly equal to 3.14 since 3.14 is not exactly representable in base 2 (float 3.14 is closer to 3.1400001).
Dolphin
This doesn't work either, just because 3.4 would result in .4 * 100 == 40, not 4
Will Eddins
A: 

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.

David
Depending on localization, the ToString() method may use a comma instead of a period.
Aistina
although you said it -1 for cheating...
bendewey
Won't work if the current culture's decimal separator isn't a period
Joe
Change the "." to System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator
Dolphin
Still not completely reliable: NumberDecimalSeparator is a string whose length may be > 1 = you'd be better specifying InvariantCulture for ToString and Parse. Also the int.Parse might throw depending on the number of digits after the decimal point. Michael's below is better
Joe
Except for how Michael's solution doesn't actual answer the question...
David
Now this is completely reliable...I don't think there's a scenario where it breaks.
David
Every time you use a string operation in place of a math operation, a kitten dies.
fatcat1111
A: 

To suggest something different than the others, an extension method (with a method similar to David's):

public static int GetDecimalAsInt(this float num)
{
    string s = n.ToString();
    int separator = s.IndexOf(System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator);
    return int.Parse(s.Substring(separator + 1));
}

// Usage:
float pi = 3.14;
int digits = pi.GetDecimalAsInt();

Edit: I didn't use the "best" answer, because it omitted the hardest part, which is converting an arbitrary decimal number, and did not work for negative numbers. I added the correction requested in David's answer.

Will Eddins
doesn't work for the same reasons as davids
jitter
You should choose the top answer to model.
bendewey
A: 

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.

David
A: 

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));
jitter
Your example should work for many numbers, but for the example you gave it crashes because fraction is set to 0.567890000000034 which you convert to 567890000000034 which overflows int32.
David
(which situation my second solution handles, incidentally)
David
now it should work for my own sample too
jitter
A: 

Here's another version that also tells how many digits are part of the fractional make-up, which I needed.

public static int GetFractionalPartAsInt(decimal n, out int numOfFractionalDigits)
{
  n -= Math.Truncate(n);
  n = Math.Abs(n);

  int numOfFractionalDigitsValue = 0;
  // When n != Math.Truncate(n), we have seen all fractional decimals.
  while (n != Math.Truncate(n))
  {
    n *= 10;
    numOfFractionalDigitsValue++;
  }

  numOfFractionalDigits = numOfFractionalDigitsValue;

  return (int)n;
}

It's similar in idea to David's answer (his non-cheating version). However, I used the decimal type instead of double, which slows things down, but improves accuracy. If I convert David's (again, non-cheating version) answer to use a decimal type (in which case his "precision" variable can be changed to the constant zero), my answer runs about 25% faster. Note that I also changed his code to provide the number of fractional digits in my testing.

Tyler Collier
+1  A: 

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.

Tyler Collier