#JavaScript (ES6), 100 99 98 78 bytes
JavaScript (ES6), 100 99 98 78 bytes
Takes input as a string. Returns a float.
s=>+(0+s).replace(/\d/g,(d,i)=>j&&+d+((n=s[i+!++s[i]])<2&&i?--j:n>7&&j--),j=1) ###How?
How?
We first prepend a leading \$0\$ to the input string, so that we're guaranteed to have a digit before a possible leading \$8\$ or \$9\$, that must trigger the rounding right away.
The flag \$j\$ is set to \$1\$ as long as we are looking for a digit on which we can do a satisfying rounding, and set to \$0\$ afterwards.
Because a leading \$0\$ was added to the string that we're walking through but \$s\$ was left unchanged, \$d\$ contains the current character and \$s[i]\$ is pointing to the next character.
We use the following code to load the next digit in \$n\$, skipping a possible decimal separator:
n = s[i + !++s[i]] Although strings are immutable in JavaScript, the expression ++s[i] will return \$s[i]+1\$ if it contains a numeric value, even though \$s[i]\$ is not actually incremented. Therefore, the expression !++s[i] is evaluated to \$false\$ (coerced to \$0\$) for all digits (including \$0\$) and to \$true\$ (coerced to \$1\$) for the decimal separator ".".
When the rounding occurs, we yield d + --j if the next digit \$n\$ is \$0\$ or \$1\$ (and it's not the leading digit of the original input) and d + j-- if \$n\$ is \$8\$ or \$9\$. Therefore, \$j\$ is set to \$0\$ in both cases but we add \$0\$ to \$d\$ in the first case (rounding down) and \$1\$ in the second case (rounding up).