Tech Off Thread
17 postsOdd C# multiplication behavior

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?

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.

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 noninteger values (and coincidentally, I'm the author of that library )

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 noninteger values (and coincidentally, I'm the author of that library )
I'd be interested in that if you can share a link?

phreaks said:W3bbo said:*snip*
I'd be interested in that if you can share a link?
http://sine.codeplex.com < My arbitraryprescision 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 integeronly 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.

W3bbo said:phreaks said:*snip*
http://sine.codeplex.com < My arbitraryprescision 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 integeronly 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
:/

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.

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.

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.

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"));

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?

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.

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
The last digit is kind of bogus.

Dexter said:ploe said:*snip*
Yes, it can. But try 78.884687963873147 instead. Or 78.884687963873143
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 nonexistent. Do you think it's just an odd behavior of the debugger?

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 nonexistent. 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.

W3bbo said:phreaks said:*snip*
http://sine.codeplex.com < My arbitraryprescision 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 integeronly 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

W3bbo said:phreaks said:*snip*
http://sine.codeplex.com < My arbitraryprescision 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 integeronly 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?
Comments closed
Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums, or Contact Us and let us know.