round to nearest non-zero number

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
tryhard
Posts: 6
Joined: 04 Sep 2018, 02:04

round to nearest non-zero number

07 Sep 2018, 07:56

Is there a way to round the result of an operation/expression to the nearest non-zero number?

E.g. a := x / y

Depending what x and y are, the result will have a varying number of decimal places and 0s before it gets to the first non-zero number.
If the result is e.g. 0.000456 I would like to round it to 0.0005.
If the result is e.g. 0.567 I would like to round it to 0.6.

If I use Round(), I can say how many decimal places I want, but that depends on the result of x / y. If I set it to Round(a, 1), it works fine for the second result, but the first result will be 0.0. If I set it to Round(a, 4) to catch the first result, the second will be much longer than necessary (basically, it won't be rounded at all, because it "only" has 4 decimal places).

Is there another way to do it?

Thanks!
User avatar
Exaskryz
Posts: 2882
Joined: 17 Oct 2015, 20:28

Re: round to nearest non-zero number

07 Sep 2018, 08:56

There's probably a way more efficient way. But you can identify how many 0s there are by using RegExMatch to extract the string of ".000..." for how many zeroes there are, then use the StrLen command to find how many positions to go. This counts the period in the strlen, but we want that, or else it would round a place too far to the left. In example below, it's 4.

Code: Select all

result:=0.000456
RegExMatch(result,"\.0*",zeroes)
MsgBox % Round(result, StrLen(zeroes))
return
The RegEx Needle breaks down like this: \. is a literal period. The 0* means match any number of zeroes immediately after the period. The asterisk allows none of the preceding character to be an option -- so even if there are no zeroes, like in your second example of 0.567, it'll still match the total string of . which has length 1.
wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: round to nearest non-zero number

07 Sep 2018, 09:34

If I use Round(), I can say how many decimal places I want, but that depends on the result of x / y.
Try with 1-Log(x/y) to find the number of decimal places you want:

Code: Select all

x := 456, y := 1000000
a := x / y
b := Round(a, 1-Log(a))
MsgBox, %a%`n%b%

x := 567, y := 1000
a := x / y
b := Round(a, 1-Log(a))
MsgBox, %a%`n%b%
I hope that helps.
tryhard
Posts: 6
Joined: 04 Sep 2018, 02:04

Re: round to nearest non-zero number

07 Sep 2018, 14:24

Wow! Thanks!
I tried out both versions. Incredible! Both of them saved me about 400 lines of code (lots of "if 0.0 then don't round, otherwise round" for multiple variables...). You guys would probably laugh if you saw my whole script, but hey, I'm still pretty new to this.

Right now I'm favoring Exaskryz' solution.
I tried 1-Log(a), but it rounded off too much (like 160 instead of 155).
The RegEx works great! Only problem is, I occassionaly have one decimal too many (like 69.04 where I was expeciting 69), but I might just have to live with that. I can't remove the \., and I can't think of another way to limit it properly.

My goal is to round the result of p to a maximum of one decimal place, unless that results in 0.0, in which case I want to round to the nearest non-zero number after the decimal. (I could have said that to begin with, sorry.)

Here's what I've got (just one of many in the script, but they're all the same form/principal):

Code: Select all

convertlv:
p := $1 / 28.3168467117
RegExMatch(p, "\.0*",zp)
rp := Round(p, StrLen(zp))
;rp := Round(p, 1-Log(p))
Send, {U+0020}%$1%{U+00A0}l{U+0020}^i(%rp%{U+00A0}cu{U+00A0}ft){right}{U+0020}
return
If $1 = 1955, I get 69.04 (where my target is 69), but if $1 = 2, I get 0.07 (which is perfect).

I tried adding rp2 := Round(rp, 1) and changing %rp% to %rp2% in the Send, but that doesn't quite work either:

Code: Select all

convertlv:
p := $1 / 28.3168467117
RegExMatch(p, "\.0*",zp)
rp := Round(p, StrLen(zp))
rp2 := Round(rp, 1)
;rp := Round(p, 1-Log(p))
Send, {U+0020}%$1%{U+00A0}l{U+0020}^i(%rp2%{U+00A0}cu{U+00A0}ft){right}{U+0020}
return
By "double rounding": If $1 = 1955, I get 69.0 (which is still okay, but not ideal), while if $1 = 2, I get 1 (which is not accurate enough).

Again, thanks for any help!
___________
Background, in case it helps: My whole script is a hotstrings script to convert measurements as you type (see https://autohotkey.com/boards/viewtopic.php?f=5&t=51414, which is where I started). Right now I have 14 unit/measurment conversions, plus the - / version of each one, so total of 28.

Code: Select all

hotstrings(" (\d+\.?\d*)( lv )", "convertlv") 
hotstrings("(\d+\.?\d*)(\-|\/)(\d+\.?\d*)( lv )", "convertlvb") 
return
convertlvb:
p := $1 / 28.3168467117
q := $3 / 28.3168467117
RegExMatch(p, "\.0*",zp)
rp := Round(p, StrLen(zp))
;rp := Round(p, 1-Log(p))
RegExMatch(q, "\.0*",zq)
rq := Round(q, StrLen(zq))
;rq := Round(q, 1-Log(q))
Send, %$1%%$2%%$3%{U+00A0}l{U+0020}^i(%rp%%$2%%rq%{U+00A0}cu{U+00A0}ft){right}{U+0020}
return
(the rest of the hotstrings code is from here: https://autohotkey.com/boards/viewtopic.php?f=5&t=51414, and I take no credit for it; I only wish I understood how it works!)
That's why I used zp instead of zeroes: to catch two numbers $1 and $3 with another character between them (like - or /), e.g. 25-30 km/h or 4/5 l/100 km. There are probably much more elegant ways to do convertlv and convertlvb in one block, but I'm not there yet. In any case, I needed two zeroes, so I named them zp z(eroes)p and zq z(eroes)q.
The ^i sets the converted measurement in <i></i> tags in the target editor, then {right} moves the cursor out of the italic tags so I can start typing again without the rest of my text being italic.
User avatar
Exaskryz
Posts: 2882
Joined: 17 Oct 2015, 20:28

Re: round to nearest non-zero number

07 Sep 2018, 14:56

Two improvements. Try in the RegEx method the needle "^0*\K\.0*". The ^ means we must match the beginning of the string. 0.something will match, but 160.something will not. The 0* also lets .something, with no digits left of decimal match. The \K means all matching characters in pattern to left of it are not captured in the output variable.

Testing it I get 0.000456 still rounds to 0.0005, but 160.000456 rounds to 160.

Wolf's log approach I like and can use Round(a, log(a)>1 ? 0 : 1-log(a)). This uses ternary operator -- shorthand for if/else. It reads: if log(a)>1, then use 0, else 1-log(a) for the place to round to.
tryhard
Posts: 6
Joined: 04 Sep 2018, 02:04

Re: round to nearest non-zero number

10 Sep 2018, 07:53

That is sooooo cool!
Thanks!

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: mikeyww, ulysim and 321 guests