Floating point value being returned from Number.toString

One of the users of my watch face has an odd problem in that it is displaying floating point values:


There are no Floats in the code and I have no idea how the temperature values are showing like this (e.g. the 17.777779 and 20.555555).

The code uses only Numbers:

        var temperatureString = statuteConversion(temperature).toString() +  "°";
        drawData(dc, temperatureString, _font, colourTemperature, _iconTemperature, x, y, pos);

Where:

    function statuteConversion(temp as Number) as Number {
        if(_temperatureUnits == System.UNIT_STATUTE) {
            temp = (temp * 9/5) + 32;
        }
        return temp;
    }
Oh, and they are using metric settings.
Anyone have any ideas?

Paul

  • Type checking is a compile time function, not run time, and on some devices, Garmin weather returns the temperature as a Float and not a Number,

    The easiest thing to do, is when you display the temperature, be it F or C, is to use format, like

    temp.format("%d")

  • Also, there is a bug in your celsius to fahrenheit conversion code, in the case that temp actually is a Number (or Long) and not a Float (or Double).

    temp = (temp * 9/5) + 32;

    If temp is a Number, then temp * 9 is a Number and (temp * 9) / 5 is division of two Numbers (integers), which means that integer division will be used (not what you want). In order to perform float division, at least one of the two operands has to be a float or double.

    e.g.

    6 * 9 / 5 = 54 / 5 = 10 (integer division - bad)

    6 * 9 / 5.0 = 54 / 5.0 = 10.8 (float division - good)

    Suggest:

      

    function statuteConversion(temp as Numeric) as Number {
        if (_temperatureUnits == System.UNIT_STATUTE) {
            temp = temp * 9 / 5.0 + 32;
        }
        return Math.round(temp).toNumber();
    }

    Note that in the original code, writing temp * 9/5 with spaces around * but not / is slightly misleading, as it suggests that 9/5 will be calculated first before multiplying by temp. In fact, temp * 9/5 will be evaluated left to right, which means temp * 9 will be calculated first, then the result will be divided by 5.

    I don’t recommend temp.format(“%d”) in the absence of Math.round(), as it will truncate floating point values (round towards 0), unless that’s what you actually want. e.g. 12.9 will be truncated to 12.0. Same argument goes for using toNumber() (without Math.round()).

    Interestingly enough, I’m pretty sure this exact same thread with the exact same problems has come up before (temperature conversion function which assumes the input is an integer but it’s actually floating point, and incorrect C to F conversion.)

  • Type checking is a compile time function, not run time, and on some devices, Garmin weather returns the temperature as a Float and not a Number,

    Yes, but if type checking is used properly and the applicable fields in the Garmin weather API are typed properly, then it should be impossible to call statuteConversion() with anything except a Number (at runtime) — ofc this is under the assumption that temperature is coming from the weather API in the first place. That’s the whole point of compile-time type checking after all - to prevent runtime bugs relating to incorrect types.

    Everything in the Weather module that’s related to temperature is either Numeric or Float, so I’m not sure if the types were incorrect in a previous version of the SDK, or if OP’s type checking level isn’t strict enough (or something else, like the data actually comes from a third party service and the related monkey c code isn’t typed properly).

    [https://developer.garmin.com/connect-iq/api-docs/Toybox/Weather/CurrentConditions.html]

  • That is interesting. Thank you.
    The API documentation states that it returns a Number. Disappointed

  • That is great, thank you so much.
    I did have a look through the forum to see if I could find anything similar. Disappointed

  • The API documentation states that it returns a Number

    Could you link to the doc please (and/or provide a screenshot)? If the API is returning a Float or Number but it’s documented/specified to return a Number, that’s a bug which needs to be fixed. The documentation aspect is bad, but the incorrect type specification in the API itself is worse (as it allows bugs like this to happen). (Most likely the documentation and API type specification are autogenned from the same source, so it’s not surprising that the same bug would occur in both.)

    I did have a look through the forum to see if I could find anything similar

    It’s all good, I didn’t mean to sound critical, it’s just a funny case of deja vu. Even the “9/5” spacing issue was in the previous thread, iirc

  • All of the temperature related fields for Weather.CurrentConditions in SDK 6.3.1, 6.4.0 and 6.4.2 (latest and probably final 6.x SDK) were incorrectly typed as Number. This is probably the case for every previous SDK which supported Weather and type checking, too.

    It seems that this was only actually fixed in the current 7.1.0 SDK.

  • Ah, that explains a lot. Thank you so much!

  • I think you missed the point. He posted that there's no Float involved, meaning he intended to have Number. The other argument regarding the rounding is interesting though, so maybe it's worth to do it, anyway now that Garmin turned it to Float he'll need to round anyway Slight smile

  • I think you missed the point. He posted that there's no Float involved, meaning he intended to have Number. The other argument regarding the rounding is interesting though, so maybe it's worth to do it, anyway now that Garmin turned it to Float he'll need to round anyway

    Nope, once again I did not miss the point, but I think you did.

    TL;DR you are missing the fact that Garmin likely (*) hasn’t changed the behavior of the devices, it has changed the incorrect API typing which used to claim that the Weather.CurrentConditions temperature fields were Number, but now it correctly specifies that they’re Numeric (which means Number, Long, Double or Float) or in one case, Float.

    Regardless of whether device behavior has changed or not, it seems to be an incontrovertible fact that some devices are returning Float for temperature-related fields in Weather.CurrentConditions, despite the fact that SDK 6.4.2 and earlier have those fields typed as Number.

    (*) At the very least, OP has an existing watch face (**) for which the temperature types are wrong, as evidenced in the OP and the thread. If the types in the SDK were correct, then the situation in the OP (obtaining a Float result) would be impossible.

    (**) almost certainly built on SDK 6.4.2 or lower, since it's already released and since OP said the type of the temperature fields in the SDK documentation is Number.

    Also, I seem to recall this exact situation (wrong types for weather temperature fields) has come up before.

    1) He intended to have Number as in input parameter but from the OP and the thread, you’ll see it didn’t quite work out that way.

    - based on the fact that he (and the type checker) assumed the input was Number and the details of his code, it should’ve have been impossible for him to obtain a Float as a result, but that’s what happened

    - he used the Garmin Weather module/API to get the temperature, which explains the apparent fact that the input parameter (assumed to be a Number) was sometimes actually a Float

    As you can see from the thread, the Weather API incorrectly typed temperature-related fields as Number, although they should really be Numeric or Float. (As jim_m_58 pointed out, some devices will return temperature-related fields as float). This means that even though his function *intended* to take a Number as an input, in some cases it did not. Type checking failed to catch this situation since the API was typed incorrectly.

    2) Garmin has now corrected the API types for temperature-related fields in Weather.CurrentConditions to Numeric (meaning they can be Number, Long, Double or Float — I’m sure that in practice, they’ll be either Number or float). feelsLikeTemperature was corrected to Float. 

    Again, the change in the SDK doesn’t change the behavior in the device, it only fixes the API so the correct types are specified.

    Given that the code which uses those fields needs to account for both cases (Number/int or float), this can be accomplished by changing the input parameter in OP’s code to Numeric. This would be true whether OP compiles with SDK 7.1.0 or SDK 6.4.0. Building with SDK 7.1.0 would *force* this change (or something similar), whereas building with 6.4.0 would allow the incorrect code to remain as is.

    I think we agree on the next two points, but I’ll reiterate them anyway.

    3) Regarding the C to F conversion, it’s incorrect to write temp_in_c * 9 / 5, since when temp_in_c *is* an integer (Number or Long), this will lead to integer division, which is clearly not what anyone wants when converting temperatures (I already gave an example of bad conversion using integer division above.) The easiest way to fix this is to change 9 to 9.0 (or 5 to 5.0)

    4) Regarding rounding, if you want the result in OP’s code to be a Number, there are two cases where rounding (rather than truncation) is a good idea

    - When the input is a Float (regardless of units)

    or

    - When the input is a Number but the user’s chosen units are STATUTE, since this requires a conversion from C to F, which will result in a Float

    Again, I gave the example where 12.9 rounds to 13 but truncates to 12.