Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / Visual J# / August 2004

Tip: Looking for answers? Try searching our database.

Bad math when using * operator along with Math.pow

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
webmaster@nahnet.com - 14 Aug 2004 08:19 GMT
Check out this code. I need to be able to multiply a 15 digit decimal number by 10 to the power of 15, and come up with the correct answer. Easy enough for other languages to do, but J# seems to choke on it. Here's the code.

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
}

My best guess is that it has something to do with the * operator. My guess is that it does some kind of crazy rounding, but looking at the code it doesn't make sense really. I made sure that the original value comes out right, and it does, and I trust the Math.pow function to come out right as well, but the combination of it all fails.

I'm so puzzled. HELP!

- Nick
Ianier Munoz - 15 Aug 2004 16:18 GMT
Hi Nick,

FYI, if you change the first line of your code by

double dblPi = System.Math.PI;

then it returns 857167300702112

This behavior seems to do with how the compiler translates double constants
to their binary representation.
Regards,
Signature

Ianier Munoz
http://www.chronotron.com

> Check out this code. I need to be able to multiply a 15 digit decimal
> number by 10 to the power of 15, and come up with the correct answer. Easy
[quoted text clipped - 28 lines]
> Comprehensive, categorised, searchable collection of links to ASP &
> ASP.NET resources...
Jay B. Harlow [MVP - Outlook] - 15 Aug 2004 17:50 GMT
Nick,
In addition to Ianier's comments. See the following on how doubles in .NET
really work.

http://www.yoda.arachsys.com/csharp/floatingpoint.html

http://www.yoda.arachsys.com/csharp/decimal.html

Hope this helps
Jay

> Check out this code. I need to be able to multiply a 15 digit decimal number by 10 to the power of 15, and come up with the correct answer. Easy
enough for other languages to do, but J# seems to choke on it. Here's the
code.

> private void button1_Click (Object sender, System.EventArgs e)
> {
[quoted text clipped - 10 lines]
>
> My best guess is that it has something to do with the * operator. My guess is that it does some kind of crazy rounding, but looking at the code it
doesn't make sense really. I made sure that the original value comes out
right, and it does, and I trust the Math.pow function to come out right as
well, but the combination of it all fails.

> I'm so puzzled. HELP!
>
[quoted text clipped - 3 lines]
> Sent via Fuzzy Software @ http://www.fuzzysoftware.com/
> Comprehensive, categorised, searchable collection of links to ASP & ASP.NET resources...
Nick Hauenstein - 15 Aug 2004 22:05 GMT
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
Ianier Munoz - 16 Aug 2004 04:52 GMT
java.math.BigDecimal will give you some more control over precision.
However, you'll have to implement the trig functions by yourself ( e.g.
sin(x) = x - x^3/3! + x^5/5! - x^7/7! ... +/- x^n/n! )
Regards,
Signature

Ianier Munoz
http://www.chronotron.com

> Thanks so much! I now can see what causes the code to freak out. However,
> I
[quoted text clipped - 147 lines]
> this...
> - Nick
Nick Hauenstein - 15 Aug 2004 22:22 GMT
Is there anything in J# equivalent to the toFixed() method in JScript?

Because I was playing around with JScript and this works EXACTLY like I need
the other to work:

var sngPi = 3.14159265358979;
document.write(Math.sin((121 / 180) * sngPi).toFixed(15));

Again, and as always, thanks in advance!

- Nick

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.