Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

What's going on with the >= operator?


  • Please log in to reply
15 replies to this topic

#1 Valkyrie-MT

Valkyrie-MT

    Advanced Member

  • Members
  • PipPipPip
  • 315 posts
  • LocationIndiana, USA

Posted 04 January 2011 - 02:49 AM

I found a Log function for the MicroFramework a couple months ago, but it seemed to get stuck in an infinite loop so I thought the code was bad/untested, but then I happened to try a simplified version in the emulator tonight, it worked! The same exact code fails when running on the NetduinoPlus hardware. So, I took a close look into which line is at fault when running on the netduino and what I found is that the condition for the while loop returns true for this expression: 0.0 >= 4.94065645841247E-324. Causing an infinite loop.

Here is the screenshot and the code:
Posted Image

public class Program
    {
        public static void Main()
        {
            // Result for Log(7) should be 1.9459101490553133051053527434432
            Debug.Print(Log(7, System.Math.E).ToString());
        }

        /// <summary>
        /// Calculate logaritmic value from value with given base
        /// </summary>
        /// <param name="x">a Number</param>
        /// <param name="newBase">Base to use</param>
        /// <returns>Logaritmic of x</returns>
        public static double Log(double x, double newBase)
        {
            // Based on Python sourcecode from:
            // http://en.literateprograms.org/Logarithm_Function_%28Python%29

            double partial = 0.5F;

            double integer = 0F;
            double fractional = 0.0F;

            if (x == 0.0F) return double.NegativeInfinity;
            if ((x < 1.0F) & (newBase < 1.0F)) throw new ArgumentOutOfRangeException("can't compute Log");

            while (x < 1.0F)
            {
                integer -= 1F;
                x *= newBase;
            }

            while (x >= newBase)
            {
                integer += 1F;
                x /= newBase;
            }

            x *= x;

            while (partial >= double.Epsilon)
            {
                if (x >= newBase)
                {
                    fractional += partial;
                    x = x / newBase;
                }
                partial *= 0.5F;
                x *= x;
            }

            return (integer + fractional);
        }

    }

So, as a workaround, I changed the last while loop line to this:
while (partial >= double.Epsilon && partial != 0.0)

And now it works, but I am baffled why it doesn't work as originally written. My concern is that there is a precision issue in here somewhere...

P.S. This code originally came from here

-Valkyrie-MT

#2 Omar (OZ)

Omar (OZ)

    Advanced Member

  • Members
  • PipPipPip
  • 564 posts

Posted 04 January 2011 - 02:55 AM

Thats a really small number... does it need to be that precise ?

#3 Valkyrie-MT

Valkyrie-MT

    Advanced Member

  • Members
  • PipPipPip
  • 315 posts
  • LocationIndiana, USA

Posted 04 January 2011 - 03:44 AM

Thats a really small number... does it need to be that precise ?


A double is a double... They all have the same precision, so I would think the smallness of double.Epsilon should be fine. Also, this is a framework provided constant, not mine. I actually know very little about how log functions work. I push the magic button on the calculator and it works. Or I use Math.Log when coding. But in this case, all I've got is this code. I'm not too concerned with making the log function work since I can get it to give me the right value, but I would like to know why that comparison is giving me different results in hardware. I have many other comparisons in my code and I just want to understand, what's the difference?

Also, here is a simpler example:
double value = 0.0;
Debug.Print((value >= double.Epsilon).ToString());
The above code incorrectly returns true on the Netduino and false in the emulator.

Now, eliminate the variable:
Debug.Print((0.0 >= double.Epsilon).ToString());

The above code correctly returns false on both the Netduino and the emulator.

I think the only difference is one is a double to single comparison and the other is a double to double. Interesting...

Update: They are both double to double comparisons and even adding the "D" postfix does not change the results. :huh:

-Valkyrie-MT

#4 Nevyn

Nevyn

    Advanced Member

  • Members
  • PipPipPip
  • 1072 posts
  • LocationNorth Yorkshire, UK

Posted 04 January 2011 - 08:38 AM

A double is a double...


This is a classic problem in numerical computing. I'd try changing the condition to:

(partial >= (double.Epsilon * 2))

This will give a small loss of precision in the answer but you are down at the extreme limits of the algorithm anyway so is it really that important?

Regards,
Mark

To be or not to be = 0xFF

 

Blogging about Netduino, .NET, STM8S and STM32 and generally waffling on about life

Follow @nevynuk on Twitter


#5 Charles

Charles

    Advanced Member

  • Members
  • PipPipPip
  • 192 posts

Posted 05 January 2011 - 12:22 AM

Yes, it is that important. There is an obvious bug here somewhere that should be fixed - who knows what else it might effect in the system. Reminds me of Intel's FDIV bug in the early Pentium processors - Let's try to handle this bug's discovery a little better than Intel did. Chris?

#6 ClosedEyesSeeing

ClosedEyesSeeing

    Member

  • Members
  • PipPip
  • 21 posts
  • LocationVirginia

Posted 05 January 2011 - 02:19 AM

Trying to give anything that might help. I notice that Parse sets the Epsilon value to 0 both by hand and by giving it the value itself (in string form).

double tester = double.Parse("4.9406564584124654e-324");
Debug.Print(double.Epsilon.ToString());
Debug.Print(tester.ToString());

tester = double.Parse(double.Epsilon.ToString());

Debug.Print(tester.ToString());

Results:
4.9406564584124654e-324
0
0

I'll do some more tests and see what I can find.

ClosedEyesSeeing

#7 ClosedEyesSeeing

ClosedEyesSeeing

    Member

  • Members
  • PipPip
  • 21 posts
  • LocationVirginia

Posted 05 January 2011 - 02:44 AM

Parse works fine at 4.9406564584124647e-307 but as soon as you hit 4.9406564584124647e-308 the parse sets it to 0.

Test Code:

     public static void Main()
        {            
            int startVal = 100; // I know 100 to work
            double tester;
            bool isAlive = true;

            while (isAlive)
            {
                tester = double.Parse("4.9406564584124654e-" + startVal.ToString());                
                Debug.Print(tester.ToString());

                if (tester == 0)
                    isAlive = false;
                else
                    startVal++;
            }

            Debug.Print("startVal is " + startVal.ToString());            

        }


#8 Valkyrie-MT

Valkyrie-MT

    Advanced Member

  • Members
  • PipPipPip
  • 315 posts
  • LocationIndiana, USA

Posted 05 January 2011 - 03:36 AM

Parse works fine at 4.9406564584124647e-307 but as soon as you hit 4.9406564584124647e-308 the parse sets it to 0.


That is really interesting. I read a little about this epsilon stuff on wikipedia, and from what I read there, it looks like Epsilon for the double should be 1.11e-16, but it is 4.94065645841247E-324 in both regular and micro .NET Framework. So, I took the java code for calculation of Epsilon and ran it on my netduino and got an approximation of 2.2204460492503131e-16 which is close to the 1.11e-16 and no where near the double.Epsilon value of 4.94065645841247E-324. So now, I am wondering if I am just exceeding the precision of the hardware and the constant (double.Epsilon) is just plain wrong? When I go back and use 1.11e-16 as my epsilon, netduino returns the correct answer. But, from references online: double - 8 bytes IEEE 754. Covers a range from 4.94065645841246544e-324d to 1.79769313486231570e+308d (positive or negative).

So why is the calculated Epsilon so far off from the double.Epsilon constant?

Here is my conversion of the java code from wikipedia for determining an Epsilon approximation:

public static void Main()
        {
            // write your code here

            calculateMachineEpsilonFloat();
            calculateMachineEpsilonDouble();

        }

        private static void calculateMachineEpsilonFloat() 
        {
            float machEps = 1.0f;
 
            do {
                machEps /= 2.0f;
            }   
            while ((float)(1.0 + (machEps/2.0)) != 1.0);
 
            Debug.Print( "Calculated Float Machine epsilon: " + machEps );

            // Emulator and netduino float result: 1.1920929e-007

        }

        private static void calculateMachineEpsilonDouble()
        {
            var machEps = 1.0;

            do
            {
                machEps /= 2.0;
            }
            while ((double)(1.0 + (machEps / 2.0)) != 1.0);

            Debug.Print("Calculated Double Machine epsilon: " + machEps);

            // Emulator and netduino double result: 2.2204460492503131e-16

        }

-Valkyrie-MT

#9 Valkyrie-MT

Valkyrie-MT

    Advanced Member

  • Members
  • PipPipPip
  • 315 posts
  • LocationIndiana, USA

Posted 05 January 2011 - 03:56 AM

So why is the calculated Epsilon so far off from the double.Epsilon constant?


Eureka! double.Epsilon is the wrong value to use for comparisions in C#. It is apparently a very poorly named constant. See the discussion here. So ultimately, the code is doing a comparison that is beyond the limit of precision (* I think *) when using double.Epsilon. So, the correct epsilon to be used in this case is.... 2.22045e-016

And that does seem to return a correct result. I guess if you use a smaller number, it's a crap shoot as to whether you'll get the correct answer...

My guess is that MS screwed up when they originally put that constant in the framework and decided that changing it could cause more harm than good, so they changed the definition instead.

-Valkyrie-MT

#10 ClosedEyesSeeing

ClosedEyesSeeing

    Member

  • Members
  • PipPip
  • 21 posts
  • LocationVirginia

Posted 05 January 2011 - 03:59 AM

it is 4.94065645841247E-324 in both regular and micro .NET Framework.


It seems Microsoft might have a history of this. Specifically in that last post from Microsoft:

We couldn't change the value of the current Double.Epsilon without breaking people who rely on it, and we aren't planning to add a new property for .NET Framework 4.0. But continuing to get votes and comments on this issue will help us prioritize this against other requests and potential features for the next release.


It's currently marked as resolved, but I don't much trust that at the moment.

ClosedEyesSeeing

#11 Valkyrie-MT

Valkyrie-MT

    Advanced Member

  • Members
  • PipPipPip
  • 315 posts
  • LocationIndiana, USA

Posted 05 January 2011 - 04:17 AM

ClosedEyesSeeing,

Nice find, I had not seen those. So the final answer is --

Don't use double.Epsilon, use 2.22045e-16 instead.

I'm just happy no firmware update is needed to fix this and I don't have to worry about the netduino doing comparisons incorrectly.

It would be nice to start a collection of math functions. Maybe on codeplex. Perhaps I can ask Elze if he's ok with using his exMath.cs as the starting point.

Update: Elze never got back to me. Also, here is the final version of the Log function that I use today on my Netduino:

        /// <summary>
        /// Calculate logaritmic value from value with given base
        /// </summary>
        /// <param name="x">a Number</param>
        /// <param name="newBase">Base to use</param>
        /// <returns>Logaritmic of x</returns>
        public static double Log(double x, double newBase)
        {
            // Based on Python sourcecode from:
            // http://en.literateprograms.org/Logarithm_Function_%28Python%29

            double partial = 0.5;

            double integer = 0;
            double fractional = 0.0;
            double epsilon = 2.22045e-16;

            if (x == 0.0) return double.NegativeInfinity;
            if ((x < 1.0) & (newBase < 1.0)) throw new ArgumentOutOfRangeException("can't compute Log");

            while (x < 1.0)
            {
                integer -= 1;
                x *= newBase;
            }

            while (x >= newBase)
            {
                integer += 1;
                x /= newBase;
            }

            x *= x;

            while (partial >= epsilon)
            {
                if (x >= newBase)
                {
                    fractional += partial;
                    x = x / newBase;
                }
                partial *= 0.5;
                x *= x;
            }

            return (integer + fractional);
        }

-Valkyrie-MT

#12 ClosedEyesSeeing

ClosedEyesSeeing

    Member

  • Members
  • PipPip
  • 21 posts
  • LocationVirginia

Posted 05 January 2011 - 04:21 AM

I'm just happy no firmware update is needed to fix this and I don't have to worry about the netduino doing comparisons incorrectly.



I agree, glad we gained a little bit of insight in the process. :)

#13 ColinR

ColinR

    Advanced Member

  • Members
  • PipPipPip
  • 142 posts
  • LocationCape Town, South Africa

Posted 02 February 2012 - 06:28 AM

In case it helps someone, a temperature bead was added to my Netduino last night, and I needed a Log function. (using code from here) Valkyrie-MT your function worked perfectly. Where I struggled was with the base. I needed to use the Natural Logarithm base of 2.718281828 to get the right result, so:

Log(tempBead.Read(), 2.718281828);


#14 Edward

Edward

    Advanced Member

  • Members
  • PipPipPip
  • 38 posts
  • LocationLondon, UK

Posted 25 March 2012 - 06:04 AM

Where I struggled was with the base. I needed to use the Natural Logarithm base of 2.718281828 to get the right result, so:

Log(tempBead.Read(), 2.718281828);


That base constant 'e' is in the math library as System.Math.E

HTH

#15 Arbiter

Arbiter

    Advanced Member

  • Members
  • PipPipPip
  • 132 posts
  • LocationBrisbane, Australia

Posted 27 March 2012 - 12:28 PM

Would you believe I answered this very question on Stack Overflow a couple of years ago? It could be a whole lot worse - we live in a world were floating point implementations are so good and so complete and so ubiquitous that none of you has ever needed to read the IEEE specification (otherwise you would have known all about FP and epsilon values). That's a good thing.
One day, all this too shall parse.

#16 Stefan W.

Stefan W.

    Advanced Member

  • Members
  • PipPipPip
  • 153 posts

Posted 27 March 2012 - 04:49 PM

The IEEE specification does not mention any epsilon, and as you've seen most people actually have less knowledge about floating points than they'd need - also there are severe misconceptions like

A double is a double... They all have the same precision

flying around ... leading to bugs like this ;)
I believe that no discovery of fact, however trivial, can be wholly useless to the race, and that no trumpeting of falsehood, however virtuous in intent, can be anything but vicious.
-- H.L. Mencken, "What I Believe"




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.