Math.PI accuracy

PI const (accoring to documentation) shows as 3.14159265358979323846.(developer.garmin.com/.../Math.html)

However, when you use this it's showing as 3.14159274101257324219..?

I am using SDK 3.1.9; printing using: System.println("PI is "+Math.PI.format("%.20f"));

  • Math.PI is assigned the value 3.14159265358979323846.. The value is initialized exactly like the documentation shows (the documentation is taken from the source code). It looks like this...

    module Math {
    
        const PI = 3.14159265358979323846;
        
    }

    As you've noticed, floating point constants are stored using a single precision floating point representation (32-bit float) if you use the f suffix or do not provide a suffix at all. The result is that the value of Math.PI is not as precise as you are expecting and it is not exactly equal to the value you see in the initializer.

    This isn't necessarily a bug. All representations of PI stored in an IEEE floating point format are really just estimates. The more bits available the better the estimate, but it is never exact.

    I think that if you want to get a better value for PI you should declare it as a double for yourself.

    I don't think it is unreasonable for us to update the declaration to explicitly specify that PI (and E) are Float and then update the documentation, but I think it would be a bad idea for us to change the value to be a Double. The big reasons are:

    1. a Double requires a heap allocation (a Float is 5 bytes, but a Double is at least 13 bytes... I can't recall the exact size, but you can do some heap testing to determine this for yourself)
    2. Arithmetic expressions where one or more argument is a Double result in a value that is also a Double.

    The result of this is that changing to a Double would cause existing applications to use more memory than they used previously, and they would run (slightly) slower as well. That would not go over well.

  • Thanks Travis - appreciated. I agree on the double - 32bit should be good enough!

    I've had a quick look and a 32bit float will indeed have the "less accurate" value - ie "3.1415927410157324"

    0 10000000 10010010000111111011011 = 4049 0fdb16 ≈ 3.14159274101257324 ≈ π ( pi )

    1sign 8exp 23frac = 32bit signed

    However, when I do: "if(Math.PI==3.14159265358979323846) " this equates to true.

    Does this essentially convert "3.14159265358979323846" to a 32bit signed Integer, then do the comparison? As I see it, this must be the case.

    So, to sum up, operations on PI are done on "3.14159265358979323846" but if you want to see PI, then you'll only ever see "3.14159265358979323846".

    I've tried to convert PI to a double BUT I still see the "less accurate" value.

    Thanks.

  • However, when I do: "if(Math.PI==3.14159265358979323846) " this equates to true.

    Which makes sense, as you're comparing the numbers as 32 bit floats. When compiled, 3.14159265358979323846 is a 32bit float

    Before Travis' post I thought that Math.PI was a 64bit double, but it's not.  It's a 32bit float

  • However, when I do: "if(Math.PI==3.14159265358979323846) " this equates to true.

    The comparison evaluates to true because it is true...

    1. Math.PI is a Float (a 32-bit floating point value) with the value 3.14159274101257324
    2. The literal 3.14159265358979323846 is also a Float with the value 3.14159274101257324.

    The reason that it is confusing for you is that you see Math.PI is described as having the value 3.14159265358979323846, but it actually has the value 3.14159274101257324.

    So, your next question should be... why does Math.PI not have the value 3.14159265358979323846?

    As I said above, "floating point constants are stored using a single precision floating point representation (32-bit float) if you use the f suffix or do not provide a suffix at all". In MonkeyC, the literal 3.14159265358979323846 is a 32-bit float because there is no explicit type suffix. It may look like it will have the value 3.14159265358979323846, but that value cannot accurately be represented in a 32-bit float, so the actual value will be the nearest representable 32-bit floating-point value which is 3.14159274101257324f. 

    Does this essentially convert "3.14159265358979323846" to a 32bit signed Integer, then do the comparison? As I see it, this must be the case.

    No, nothing here is converted to a 32-bit signed integer. Again, the literal 3.14159265358979323846 has no suffix to specify the type, so it is assumed to be a Float, and it will be the closest representable value which is 3.14159274101257324.

    So, to sum up, operations on PI are done on "3.14159265358979323846" but if you want to see PI, then you'll only ever see "3.14159265358979323846".

    No. Math.PI is a MonkeyC Float type (a IEEE 32-bit floating point representation), so it never had the value you want/expect.

    I've tried to convert PI to a double BUT I still see the "less accurate" value.

    You can't take something with less precision and convert it to something with more precision. Numbers just don't work that way.

    If you want a IEEE 64-bit floating point representation for PI, you need to declare it yourself.

    const DBL_PI = 3.14159265358979323846d; // note the 'd' at the end

  • Final reply, I promise.

    I did mean float, not integer, above, but you summed it up perfectly with

    "The reason that it is confusing for you is that you see Math.PI is described as having the value 3.14159265358979323846, but it actually has the value 3.14159274101257324"

    Slight smile

    I was pulling my hair out - My confusion did originate from the above, based on reading the documentation.

    Can I suggest:

    "PI = 3.14159265358979323846, however as the constant is stored as a 32bit signed float, the vaue is constrained to 3.14159274101257324."

    lol

    Thankfully, I'm not bothered about E as it doesn't affect my code Slight smile

    Appreciate your time and efforts, Jim and Travis.