How can I round a value to 2 digits

From a rule, I receive the following values from 2 different items (it’s the delta between 2 times):
-0.000000000000000125
-0.840000152587890812

I would like to see that the first value would become -0.00 and the second one 0.84.
Already tried with intValue & floatValue, but that didn’t gave the correct answer.

using js transform and .toFixed(2) maybe work

Math.round(number*100.0)/100.0

Or in the rule do something like

Math::round(value * 100) / 100

Just wrote it from the top of my head, so the syntax might be wrong and you maybe need to convert it after the rounding, but something like this should work.

Beat me to it!

Why?

By this I mean are you doing some sort of calculation that requires only two decimal points? Or do you just want to see only two decimal places in your sitemap or logs?

This is important because a little know fact about CPUs is they are really terrible at floating point math and even worse, it is impossible to even represent some numbers. Oh, it can get really close which leaves you with a value like -0.840000152587890812 but it almost never can get exact.

The proper way to deal with this is to simple live with the numbers having lots and lots fo decimal places (i.e. don’t round the number) until the very last moment, usually when the number is presented to a human.

In your sitemap you would use:

"My label [%.2f]" which will only print the two decimal points.

While we are on this note, this is the same reason why you should never try to use == with floating point numbers. They will almost never be equal.

3 Likes

I’ll add some more info:
In my openHAB setup, I’ve a lot of Xiaomi temperature sensors. Once a while some of them goes offline (it’s the connection with the gateway, not with openHAB). They only come online again after connecting them again to the gateway.
So I want to receive a notification/mail when this happens.

I thought to check the delta (deltaSince) of the temperatures onze a day: when one of them is 0, something is not correct (I have persistence on for every 5 minutes, so updatedSince is not an option).

I’m now displaying the delta of one of my sensor which is offline since 30 hours (afterwards, if all works, I’ll apply the full rule to a whole group):

logInfo("XIAOMI.CHECK", "status wasa: " + Xiaomi_TH_wasa_Temperature.deltaSince(now.minusHours(10),"jdbc" ))

This result -0.000000000000000125 and this should be 0, so I though to round this to 4 or 5 numbers…

But maybe there’s a better way to check it things are offline?

edit:
I’m checking this doc atm: https://github.com/openhab/openhab-docs/blob/gh-pages/configuration/rules-dsl.md#thing-based-triggers
Is it also possibly to grab the current state of a thing, so not only when a thing changes of status?

Probably the easiest would be to use the expire binding to set the Item to NULL or some other noticable value (e.g. -1) after the Item hasn’t been updated for a time. It is best to set the expire time to 2x the update time so if the Xiaomi updates every 10 minutes set the expire binding to 20 minutes.

You can set a rule that triggers on changes to this item and first thing in the rule check whether it is e the expired value and alert or whatever as appropriate.

There is also [Deprecated] Design Patterns: Generic Is Alive which is a more comprehensive solution but it is a little more complicated than you need right here and I need to rewrite that DP so if you want to take that approach, wait until next week when I have a chance to rewrite it.

I also thought about the expire binding, but when I write the values every 5 minutes to my db, it receives an update, even if the value is the same.
Or is there a difference between writing a value which can be the same as the previous one and an update?

They are completely separate. Updates can be saved to the database but saving to the database does not cause updated to the item. And I’m pretty sure that NULL doesn’t get saved to the database so that that into consideration for what you set the item to with expire.

I added expire="10s" for testing. The state of this item changes to UNDEF after 10 seconds, but the group where this item is member of, always reports UNDEF

Group:Number:OR(UNDEF,0) Grp_Xiaomi_sensor_check

I don’t think you can use OR with a Number type. What would you expect it to be?\

But, if we assume it works, you probably need to use NULL instead of UNDEF.

As written, again assuming it works, Grp_Xiaomi_sensor_check will be 0 if there are no UNDEF Items and UNDEF if there is one or more UNDEF Items. But since UNDEF really isn’t a valid Item state I’m not sure what it would do.

This approach will probably not work then…

I first tried to give my sensor a unrealistic value (‘999’), I used MAX for the group, so when the group is 999, I know there’s something wrong. But I don’t want that it also writes this value in my db (my graphs are f*cked up now :slight_smile:)

It might work with NULL instead of UNDEF.

You could use a proxy Item and a Rule instead of the Group to keep track of whether a sensor is offline or reporting a bad value or something. But over all I suspect you can’t do this with just a Group.

var Double pi=3.14159265359
String.format("%.2f", pi)

… can be used to shorten doubles to 2 digits, e.g. during output within rules.

Looking for some help to round to 2 dec. places.

Here’s the question
My log entry is this

2019-01-14 13:27:14.980 [INFO ] [pse.smarthome.model.script.wmachine ] - Power avarage is 1.675235979963836

from this in my rule.

val Number DnAvg = tplinksmarthome_hs110_B1AFF9_power.averageSince(now.minusMinutes(3)) as Number
    
    logInfo("dryer ", "Power avarage is: {} ", DnAvg)

And I would like it to be this.

Power avarage is 1.67

And this is my items file def for that value.

Number DnAvg            "Dryer Power Avg"                                    (BF_LaundryRoom , GetPower)

Could this be formatted at the items file level for logging and persistence, to fix this issue?

Thanks for any help.

No, an Number is a Number, Make no difference to the system.
You, however as a Human, would like 2 decimals:

The item:

Number DnAvg            "Dryer Power Avg [%.2f kW]"                                    (BF_LaundryRoom , GetPower)

In the rule:
Combine the answer from 13d ago and your logINfo:
(By the way, average not avarage!!)

logInfo("dryer ", "Power average is: " + String.format("%.2f", DnAvg))

Tried that and I got an .

2019-01-14 14:24:07.739 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Dryer Consumption State Machine': f != org.eclipse.smarthome.core.library.types.DecimalType


Try that:

logInfo("dryer ", "Power average is: " + String::format("%.2f", DnAvg))

Same error???

2019-01-14 15:21:11.682 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Dryer Consumption State Machine': f != org.eclipse.smarthome.core.library.types.DecimalType