Tech Off Thread

17 posts

Forum Read Only

This forum has been made read only by the site admins. No new threads or comments can be added.

Odd C# multiplication behavior

Back to Forum: Tech Off
  • User profile image
    ploe

    When I run this bit of code '-78.884687963873148 * Math.Pow(10, 13)' it returns the value: -788846879638731.5. I would expect it to return -788846879638731.48. Is there an alternative to the way I'm doing it above to get it to return the expected value?

  • User profile image
    Dexter

    There's nothing odd about C#'s multiplication. This is normal when using float and double data types because:

     

    - These types have a finite precision (~15 digits for double) and your values are near this precision limit

    - These types use base 2 instead of base 10. Maybe to you it seems that multiplying with Math.Pow(10, 13) should just move the decimal point but that's not possible because the internal representation uses base 2.

     

    You could try to use the decimal type instead of double but there's no Math.Pow overload for decimal. If 10 and 13 are really constants then you can use a constant instead of Math.Pow. Otherwise you'll have to write you're own Pow function.

     

  • User profile image
    W3bbo

    Dexter's right: you're dealing with problems inherent with the Single and Double types. If numerical accuracy is important to you then you should use an arbitrary precision library. There's only a few for .NET and only one supports non-integer values (and co-incidentally, I'm the author of that library Tongue Out )

  • User profile image
    phreaks

    W3bbo said:

    Dexter's right: you're dealing with problems inherent with the Single and Double types. If numerical accuracy is important to you then you should use an arbitrary precision library. There's only a few for .NET and only one supports non-integer values (and co-incidentally, I'm the author of that library Tongue Out )

    I'd be interested in that if you can share a link?

  • User profile image
    W3bbo

    phreaks said:
    W3bbo said:
    *snip*

    I'd be interested in that if you can share a link?

    http://sine.codeplex.com <-- My arbitrary-prescision library for .NET; note that I did some major refactoring in the latest revision and my code is unstable, unusable, gives inaccurate (if not just plain wrong) results, and is otherwise unfit for purpose. Use with caution. The earlier revisions (and the available download release) still work fine.

     

    http://intx.codeplex.com <-- An integer-only library for .NET far, far superior to my own... but only does integers. The IntX class in this library is superior to the BigInteger class in .NET4.0 in most circumstances, so it's worth checking out.

     

    EDIT: I see that F# comes with its own BigRational class. Note that it's a rational number (that is, any number representable by an integer numerator and denominator) so whilst it will always give you 'purer' results than a floating/decimal library like mine it won't necessarily be faster.

  • User profile image
    ploe

    W3bbo said:
    phreaks said:
    *snip*

    http://sine.codeplex.com <-- My arbitrary-prescision library for .NET; note that I did some major refactoring in the latest revision and my code is unstable, unusable, gives inaccurate (if not just plain wrong) results, and is otherwise unfit for purpose. Use with caution. The earlier revisions (and the available download release) still work fine.

     

    http://intx.codeplex.com <-- An integer-only library for .NET far, far superior to my own... but only does integers. The IntX class in this library is superior to the BigInteger class in .NET4.0 in most circumstances, so it's worth checking out.

     

    EDIT: I see that F# comes with its own BigRational class. Note that it's a rational number (that is, any number representable by an integer numerator and denominator) so whilst it will always give you 'purer' results than a floating/decimal library like mine it won't necessarily be faster.

    Okay I seem to have grasped a better understanding of the problem I'm running into:

     

    The initial type I have is a double with the value -78.884687963873148. I typecast it to a decimal because I need slightly more precision on it based on the math I'm going to do. But typecasting the double -78.884687963873148 to a decimal, I actually lose precision.

    (decimal)-78.884687963873148 = -78.8846879638731

     

    :/

  • User profile image
    W3bbo

    ploe said:
    W3bbo said:
    *snip*

    Okay I seem to have grasped a better understanding of the problem I'm running into:

     

    The initial type I have is a double with the value -78.884687963873148. I typecast it to a decimal because I need slightly more precision on it based on the math I'm going to do. But typecasting the double -78.884687963873148 to a decimal, I actually lose precision.

    (decimal)-78.884687963873148 = -78.8846879638731

     

    :/

    I think that's because Decimal's ToString() method by default truncates the results.

     

    Try working with Decimals throughout your program though.

  • User profile image
    Dexter

    ploe said:
    W3bbo said:
    *snip*

    Okay I seem to have grasped a better understanding of the problem I'm running into:

     

    The initial type I have is a double with the value -78.884687963873148. I typecast it to a decimal because I need slightly more precision on it based on the math I'm going to do. But typecasting the double -78.884687963873148 to a decimal, I actually lose precision.

    (decimal)-78.884687963873148 = -78.8846879638731

     

    :/

    Wait a moment, where did that value come from anyway?

     

    It has 17 significant digits and that's more than double can represent. Is that a constant in your code? In that case you need to make it a decimal in the first place. Like:

     

    const decimal x = -78.884687963873148m;

     

    Note the m suffix. That tells the compiler it is a decimal literal. Otherwise double is implied.

     

     

  • User profile image
    ploe

    W3bbo said:
    ploe said:
    *snip*

    I think that's because Decimal's ToString() method by default truncates the results.

     

    Try working with Decimals throughout your program though.

    Unfortunately the double comes from an external assembly.

     

    (-78.884687963873148M).ToString() is not truncated though, so I feel like I'm losing data in the typecast.

     

    The only thing that seems to convert it correctly is if I do this:
    decimal d = Decimal.Parse((-78.884687963873148).ToString("R"));

    ...not exactly efficient.

  • User profile image
    ploe

    Dexter said:
    ploe said:
    *snip*

    Wait a moment, where did that value come from anyway?

     

    It has 17 significant digits and that's more than double can represent. Is that a constant in your code? In that case you need to make it a decimal in the first place. Like:

     

    const decimal x = -78.884687963873148m;

     

    Note the m suffix. That tells the compiler it is a decimal literal. Otherwise double is implied.

     

     

    It's definately a double, I got the value from the assembly as a double with that value in it. I've just been using a constant in my posts so that it's more understandable.

     

    If you put that value in your watch of VS it does say the type is a double.

     

    It's all a little confusing. If you run this bit of code you see that a double can store this value:

    double d = -78.884687963873148;

    Console.WriteLine(d.ToString("R"));

  • User profile image
    itsnotabug

    are you only doing base10 scientific notation? could you get away with convert to string, save the index of ".", multiply that index by exponent, insert "." at new index, convert back to dec?

     

     

  • User profile image
    Dexter

    ploe said:
    W3bbo said:
    *snip*

    Unfortunately the double comes from an external assembly.

     

    (-78.884687963873148M).ToString() is not truncated though, so I feel like I'm losing data in the typecast.

     

    The only thing that seems to convert it correctly is if I do this:
    decimal d = Decimal.Parse((-78.884687963873148).ToString("R"));

    ...not exactly efficient.

    Well, it's quite possible to lose precision during conversion from double to decimal. That conversion requires some arithmetic operations to be performed on the double value and as you have already seen such operations are not exact.

     

    It's nice that the ToString/Parse trick works but don't rely on it to work on any double value, it's really not possible to represent exactly all double values in the decimal system.

     

  • User profile image
    Dexter

    ploe said:
    Dexter said:
    *snip*

    It's definately a double, I got the value from the assembly as a double with that value in it. I've just been using a constant in my posts so that it's more understandable.

     

    If you put that value in your watch of VS it does say the type is a double.

     

    It's all a little confusing. If you run this bit of code you see that a double can store this value:

    double d = -78.884687963873148;

    Console.WriteLine(d.ToString("R"));

    If you run this bit of code you see that a double can store this value:

    double d = -78.884687963873148;

    Console.WriteLine(d.ToString("R"));

     

    Yes, it can. But try -78.884687963873147 instead. Or -78.884687963873143 Smiley

     

    The last digit is kind of bogus.

  • User profile image
    ploe

    Dexter said:
    ploe said:
    *snip*

     

    Yes, it can. But try -78.884687963873147 instead. Or -78.884687963873143 Smiley

     

    The last digit is kind of bogus.

    You're right! Those other values don't work. The '8' is bogus and it appears as though the '4' is also non-existent. Do you think it's just an odd behavior of the debugger?

  • User profile image
    Dexter

    ploe said:
    Dexter said:
    *snip*

    You're right! Those other values don't work. The '8' is bogus and it appears as though the '4' is also non-existent. Do you think it's just an odd behavior of the debugger?

    Odd debugger behavior? Nope. It just shows that you are reaching the precision limit of the double data type.

     

    At this point one should ask why do you care if the result is -788846879638731.48 or -788846879638731.5 in the first place.

  • User profile image
    phreaks

    W3bbo said:
    phreaks said:
    *snip*

    http://sine.codeplex.com <-- My arbitrary-prescision library for .NET; note that I did some major refactoring in the latest revision and my code is unstable, unusable, gives inaccurate (if not just plain wrong) results, and is otherwise unfit for purpose. Use with caution. The earlier revisions (and the available download release) still work fine.

     

    http://intx.codeplex.com <-- An integer-only library for .NET far, far superior to my own... but only does integers. The IntX class in this library is superior to the BigInteger class in .NET4.0 in most circumstances, so it's worth checking out.

     

    EDIT: I see that F# comes with its own BigRational class. Note that it's a rational number (that is, any number representable by an integer numerator and denominator) so whilst it will always give you 'purer' results than a floating/decimal library like mine it won't necessarily be faster.

    Thanks Smiley

  • User profile image
    Ion Todirel

    W3bbo said:
    phreaks said:
    *snip*

    http://sine.codeplex.com <-- My arbitrary-prescision library for .NET; note that I did some major refactoring in the latest revision and my code is unstable, unusable, gives inaccurate (if not just plain wrong) results, and is otherwise unfit for purpose. Use with caution. The earlier revisions (and the available download release) still work fine.

     

    http://intx.codeplex.com <-- An integer-only library for .NET far, far superior to my own... but only does integers. The IntX class in this library is superior to the BigInteger class in .NET4.0 in most circumstances, so it's worth checking out.

     

    EDIT: I see that F# comes with its own BigRational class. Note that it's a rational number (that is, any number representable by an integer numerator and denominator) so whilst it will always give you 'purer' results than a floating/decimal library like mine it won't necessarily be faster.

    > note that I did some major refactoring in the latest revision and my code is unstable, unusable, gives inaccurate (if not just plain wrong) results

     

    I can haz source control? Big Smile

Conversation locked

This conversation has been locked by the site admins. No new comments can be made.