Re: Bad math when using * operator along with Math.pow
From: Nick Hauenstein (root_at_spudge.com)
Date: 08/15/04
- Next message: Nick Hauenstein: "Re: Bad math when using * operator along with Math.pow"
- Previous message: Jay B. Harlow [MVP - Outlook]: "Re: Bad math when using * operator along with Math.pow"
- In reply to: Jay B. Harlow [MVP - Outlook]: "Re: Bad math when using * operator along with Math.pow"
- Next in thread: Ianier Munoz: "Re: Bad math when using * operator along with Math.pow"
- Reply: Ianier Munoz: "Re: Bad math when using * operator along with Math.pow"
- Messages sorted by: [ date ] [ thread ]
Date: Sun, 15 Aug 2004 14:05:17 -0700
Thanks so much! I now can see what causes the code to freak out. However, I
wonder if there is a way to code around this behavior, or to deal with it.
Let me explain the importance first.
The code itself is some test code I wrote up while searching out an elusive
bug in a Pseudo-Random number generator for an encryption program. I need
to be able to produce exactly the same random sets of numbers that other
implementations of the generator produce so the encrypted data can be
decrypted no matter who's implementation of the algorithm is used.
I'm basing the J# version off a VB6 implementation, interestingly enough,
and I want to ensure it is compatible with that. Thus Pi must be defined as
it is in the code, because that's the precision to which it was represented
also in the VB6 code. VB6 does the calculations as I'd expect without
issue.
The part where the J# code goes hey-wire is in the custom rounding routine
(to get around .NET's banker's rounding). The reason for the rounding
function is the results of the sin & cos functions have to be rounded to 15
digits to again match the precision in other implementations.
*****Another thing that could eliminate this whole mess would be to get a
reliable rounding function that can handle the data I throw at it.*****
If you can figure this out, I'll name my firstborn after you!
But if anyone feels like sifting through the code, here's the code for the
rounding function:
public static double OldRound(double value, int digits)
{
int sign = System.Math.Sign(value);
double scale = Math.pow(10, digits);
double round = System.Math.Abs(value);
// BAD MATH RESULTS FROM THIS CALCULATION:
round = round * scale;
round = round + .5;
round = Math.floor(round);
round = round / scale;
round = sign * round;
return round;
}
If you're interested, here's the bit of code that calls the rounding
function. Ignore any weird variable names (like sngPi when it's the double
type), I just used all the same variable names as the VB implementation to
keep everything straight when I was writing the port.
private static double dblCenterY;
private static double dblCenterX;
// It is very important that these numbers are EXACTLY the same
// in ALL implementations to allow universal compatibility.
// MAGIC NUMBERS
// KEEP PI TO THIS PRECISION, NO MORE, NO LESS
private static double sngPi = 3.14159265358979;
private static void Generate(double dblRadius, double dblTheta)
{
double sngMaxUpper = 2147483647;
double sngMaxLower = -2147483648;
// Basically what we're doing here is picking a point on a circle
// centered at (dblCenterX, dblCenterY). This point will serve as
// the center of the next circle used for this function. The
// radius of the circle is given to the function, as well as the
// angle the new point makes with the center of the orignal circle.
// This angle and radius is ultimately what determines the new
// point, and serve as our pseudo-random seeds
double dblResultX;
double dblResultY;
dblResultX = (dblRadius * OldRound(System.Math.Cos((dblTheta / 180) *
sngPi), 15)) + dblCenterX;
dblResultY = (dblRadius * OldRound(System.Math.Sin((dblTheta / 180) *
sngPi), 15)) + dblCenterY;
if (dblResultX > sngMaxUpper || dblResultX < sngMaxLower)
{
// Re-center if new X coordinate is far off the boundary of the X-axis
ReCenter(dblCenterY, 0);
}
else
{
// Otherwise, calculate the new X-coordinate
dblCenterX = dblResultX;
}
if (dblResultY > sngMaxUpper || dblResultY < sngMaxLower)
{
// Re-center if new Y coordinate is far off the boundary of the Y-axis
ReCenter(0,dblCenterX);
}
else
{
// Otherwise, calculate the new Y-coordinate
dblCenterY = dblResultY;
}
}
Or if you don't want to sort through that mess, I'll just propose the
original problem (sample data all filled in, and simplified):
private void button1_Click (Object sender, System.EventArgs e)
{
double dblPi = 3.14159265358979;
double dblTheta = 121;
double value = System.Math.Sin( (dblTheta / 180) * dblPi );
System.Console.Write(value);
System.Console.Write("\n");
System.Console.Write(Math.pow(10, 15) * value);
// Should return 857167300702113
// but instead returns 857167300702114
}
What can I do to code around this?
Thanks again so much, and thanks in advance to anyone who replies or solves
this...
- Nick
- Next message: Nick Hauenstein: "Re: Bad math when using * operator along with Math.pow"
- Previous message: Jay B. Harlow [MVP - Outlook]: "Re: Bad math when using * operator along with Math.pow"
- In reply to: Jay B. Harlow [MVP - Outlook]: "Re: Bad math when using * operator along with Math.pow"
- Next in thread: Ianier Munoz: "Re: Bad math when using * operator along with Math.pow"
- Reply: Ianier Munoz: "Re: Bad math when using * operator along with Math.pow"
- Messages sorted by: [ date ] [ thread ]