Rounding QuantityType<Temperature> values to 0.5 °C

Within OH rules, item state values of QuantityType<Temperature> have a precision of 8 places of decimals. (Which makes them i) hard to compare, ii) messy to log). So I would like to round them to 0.5 °C => Any tips?

I don’t see why?

I want “21.3°C” in my message string, not “21.28478 °C”

var message = "Temperature is " + (tempItemC.state as QuantityType<Temperature>).format("%.1f%unit%")
// returns "Temperature is 10.0°C"

from

Long story short - there is no easy way to round a Quantity, because the granularity, the “detents” that you are trying to round to all depends on the units you are expressing the quantity in at the time. Rounding identical quantities 1200.1m and 1.2001km to “integer” does not give the same result.

Well … I just want to compare 53.152344 °C (read from a setpoint item) against 53.15000000 °C (from a calculation of the desired setpoint) and conclude that they are the same value…

Cool! Many thanks.

@rossko57 has the info for how to deal with this stuff but I’ll add the why.

Believe it or not computers are bad at math. the problem is how floating point numbers are represented in memory. Long story short, there are just some numbers that a computer simply cannot represent. It can get really really close but never actually represent it.

As an analogy, consider 1/3. That number can never be represented in decimal, only approximated with 0.33333 (repeating until you give up). Computers have the same problem.

So there will be times when you’ll see a floating point number with all sorts of decimal points. It’s not that the number is known down to eight decimal places. It’s likely somewhere along the way a calculation was done that included one of those non-representable numbers so the calculation was done with something really close. Really close means lots of decimal places.

How do programmers deal with that?

  1. Never use == with a floating point number. It is almost never going to be the case that two floating point numbers are the same, especially if they are generated from a sensor or a calculation. Instead you would use > or < or calculate the difference and see that the difference is small.

  2. Never round the number except when showing the value to a human (rounding and then calculating will result in an accumulation of errors that will quickly lead to wildly wrong answers)

1 Like

Yes. I am a computer programmer…,.

Ah right. They’re not the same of course, and rounding wouldn’t always be the answer for what you’re trying to do. Let’s say you’e rounding to integer and comparing 4.499999 with 4.500001.
You really need the magnitude of difference, which is a good puzzle. Like with rounding, the difference is pretty meaningless without units as well.

What we’re missing is an ABS() function for quantities, so the easiest way is probably just do two checks.

var fred = tempA - tempB
logInfo("test", "difference " + fred)

if (fred > 0.1|°C  || fred < -0.1|°C) {
   logInfo("test", "difference too big")
}

I think that should work even if your Items are in different units.

1 Like

Many thanks for the suggestion. In the meantime I used your other suggestion to convert the values to strings using the format() trick, and then compare the strings using equals()

val a = (aItem.state as QuantityType<Temperature>).format("%.1f%unit%")
val b = (bItem.state as QuantityType<Temperature>).format("%.1f%unit%")
if (a.equals(b)) {
  ..
}

As already discussed, sometime you’ll get the wrong answer. 10.149999 with 10.150001 (a difference of 0.00002) will become 10.1 versus 10.2 and be flagged as “not equal”.
Simple rounding does not blur differences, sometimes it hides them and sometimes it highlights them.

It’s no skin off my nose, but let’s not surprise later readers :slight_smile:
Being able to do “approximate” comparisons of quantities is indeed a useful feature.

1 Like

Understood. In my case I am not too concerned about those edge cases.

PS if you are interested in the arcana of rounding, look at this Rounding - Wikipedia in particular bankers rounding which differs from mathematicians rounding in certain edge cases where trillions of dollars may depend on it…

As something almost completely off topic, this rounding problem is one reason why so much of the world’s financial systems still run on main frames running Cobol. As a language, Cobol uses a fixed point decimal number instead of floating point. So instead of sometimes resorting to “close enough” every number is precisely represented up to X decimal places (usually 2). Running the same calculation through Cobol with fixed point numbers and Python with floating point numbers will often lead to slightly different results, and those differences cost real money.

1 Like