For a reporting application demanding numeric accuracy we needed a functionality for rounding (positive) numbers given in string representation while controlling their number of significant digits. I found the standard oracle functionality somewhat lacking in this respect since it doesn’t necessarily maintain the number of significant digits, at least when using the standard chain: round(to_number(..)).

The following PL/SQL is our own string rounding function ....
, based solely on the string representaion of the number. The method used is rather obvious (and mathematically trivial) so I consider the code to be amply self explanatory. The function is fed with the floating point number in string form (p_numstr) and the number of significant digits to round it to (p_dig).

 create or replace FUNCTION Str_Round (p_Numstr IN VARCHAR2, p_Dig NUMBER) RETURN VARCHAR2 IS

&nbsp;&nbsp;&nbsp; v_Numstr VARCHAR2(30) := p_Numstr;<br />&nbsp;&nbsp;&nbsp; v_Int&nbsp;&nbsp;&nbsp; VARCHAR2(30);<br />&nbsp;&nbsp;&nbsp; v_Frc&nbsp;&nbsp;&nbsp; VARCHAR2(30);<br />&nbsp;&nbsp;&nbsp; v_Frc1&nbsp;&nbsp; VARCHAR2(30);<br />&nbsp;&nbsp;&nbsp; v_Frc2&nbsp;&nbsp; VARCHAR2(30);<br />&nbsp;&nbsp;&nbsp; v_Len&nbsp;&nbsp;&nbsp; NUMBER;<br />&nbsp;&nbsp;&nbsp; v_Ln1&nbsp;&nbsp;&nbsp; NUMBER;<br />&nbsp;&nbsp;&nbsp; v_Ln2&nbsp;&nbsp;&nbsp; NUMBER;<br />&nbsp;&nbsp;&nbsp; v_Pt&nbsp;&nbsp;&nbsp;&nbsp; NUMBER;<br />&nbsp;&nbsp;&nbsp; v_Dig&nbsp;&nbsp;&nbsp; NUMBER := p_Dig;<br />&nbsp;&nbsp;&nbsp; v_Num&nbsp;&nbsp;&nbsp; NUMBER;<br />&nbsp;&nbsp;&nbsp; v_sgn&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;VARCHAR2(1);<br /><br />BEGIN<br />&nbsp;&nbsp;&nbsp; --pos or neg?<br />&nbsp;&nbsp;&nbsp; v_sgn := SubStr(v_numstr,1,1);<br />&nbsp;&nbsp;&nbsp; IF v_sgn &lt;&gt; '-' THEN<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_sgn := '';<br />&nbsp;&nbsp;&nbsp; ELSE<br />&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -- use abs value of v_numstr in the rest<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_numstr := SubStr(v_numstr,2);<br />&nbsp;&nbsp;&nbsp; END IF;<br /><br />&nbsp;&nbsp;&nbsp; v_Pt&nbsp; := Instr(v_Numstr, '.');<br />&nbsp;&nbsp;&nbsp; v_Len := Length(v_Numstr);<br /><br />&nbsp;&nbsp;&nbsp; -- correct pathological representations (eg '.99');<br />&nbsp;&nbsp;&nbsp; IF v_Pt = 1 THEN<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Numstr := '0' || v_Numstr;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_len := v_len + 1;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_pt := 2;<br />&nbsp;&nbsp;&nbsp; ELSIF v_Pt = v_Len THEN<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Numstr := v_Numstr || '0';<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_pt := v_pt;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_len := v_len + 1;<br />&nbsp;&nbsp;&nbsp; ELSIF v_Pt = 0 THEN<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Numstr := v_Numstr || '.0';<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_pt := v_len + 1;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_len := v_len + 2;<br />&nbsp;&nbsp;&nbsp; END IF;<br /><br />&nbsp;&nbsp;&nbsp; v_Int := Substr(v_Numstr, 1, v_Pt - 1);<br />&nbsp;&nbsp;&nbsp; v_Ln1 := Length(v_Int); -- &gt;= 1<br /><br />&nbsp;&nbsp;&nbsp; v_Frc := Substr(v_Numstr, v_Pt + 1);<br />&nbsp;&nbsp;&nbsp; v_Ln2 := Length(v_Frc); -- &gt;= 1<br /><br />&nbsp;&nbsp;&nbsp; IF v_Dig &gt; v_Ln2 THEN<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -- no rounding needed; padding of zero's<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Frc := Rpad(v_Frc, v_Dig, '0');<br />&nbsp;&nbsp;&nbsp; ELSIF v_Dig = v_Ln2 THEN<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -- no action needed<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; NULL;<br />&nbsp;&nbsp;&nbsp; ELSE<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -- do the actual rounding <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Frc1 := Substr(v_Frc, 1, v_Dig);<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Frc2 := Substr(v_Frc, v_Dig + 1);<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Num&nbsp; := To_Number(v_Int || v_Frc1);<br /><br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IF To_Number(Ltrim(v_Frc2)) &gt;= Power(10, v_Ln2 - v_Dig) / 2 THEN<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Num&nbsp;&nbsp;&nbsp; := v_Num + 1; -- an integer<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Numstr := To_Char(v_Num);<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Len&nbsp;&nbsp;&nbsp; := Length(v_Numstr);<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Int&nbsp;&nbsp;&nbsp; := Nvl(Substr(v_Numstr, 1, v_Len - v_Dig),'0'); -- evt,v_len &lt; v_dig!!<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Frc&nbsp;&nbsp;&nbsp; := Substr(v_Numstr, v_Len - v_Dig + 1);<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ELSE<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Frc := v_Frc1;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; END IF;<br />&nbsp;&nbsp;&nbsp; END IF;<br /><br />&nbsp;&nbsp;&nbsp; -- test for fractional parts that became void by rounding<br />&nbsp;&nbsp;&nbsp; IF v_Frc IS NULL THEN<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Numstr := v_sgn||v_Int;<br />&nbsp;&nbsp;&nbsp; ELSE<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; v_Numstr := v_sgn||v_Int ||'.'|| v_Frc;<br />&nbsp;&nbsp;&nbsp; END IF;<br /><br />&nbsp;&nbsp;&nbsp; RETURN(v_Numstr);<br />END;
&nbsp;