Round to n Sig Figs

PHP, 130 Bytes

<?=number_format($r=round($i=$argv[1],($n=$argv[2])-ceil(log(abs($i),10))),($d=(1+floor(log(abs($r),10))-$n))<0?abs($d):0,".","");

PHP, 133 Bytes works with values <1 for the significant figures

<?=number_format($r=round($i=$argv[1],($n=$argv[2])-floor(log(abs($i),10))-1),($d=(1+floor(log(abs($r),10))-$n))<0?abs($d):0,".","");

PHP, 56 Bytes works but skip unneccessary Zeros

<?=round($i=$argv[1],$argv[2]-floor(log(abs($i),10))-1);

Someone has stolen or deleted the round function in PHP! To make the challenge more interesting. 127 Bytes

<?=ceil($x=($i=$argv[1])*10**(($r=$argv[2])-($l=floor(log(abs($i),10))+1)))-$x<=0.5?ceil($x)*10**($l-$r):floor($x)*10**($l-$r);

Python 3, 83 bytes

(similar to the PHP answer)

from math import *
def s(x,n):
 y=10**(ceil(log10(abs(x)))-n)
 return y*round(x/y)

Test cases:

tests = [(2.6754,2), (0.00034551, 4), (50237.1238, 3),
        (2374905, 1), (543.0489, 4), (15, 1), (520.3, 3), (-53.87, 2)]

print ([s(x,n) for x,n in tests])

Output:

[2.7, 0.0003455, 50200, 2000000, 543.0, 20, 520, -54]

Apart from being slightly longer, another approach that I considered:

from math import *
def s(x,n):
 z=ceil(log10(abs(x)))
 return "%.*f"%(n-z,10**z*round(x/10**z,n))

... produces an incorrect output for the input of (15, 1):

['2.7', '0.0003455', '50200', '2000000', '543.0', '10', '520', '-54']

... due to floating point imprecision in the round() function. It seems likely to me that I could find test cases that would break the "round to zero decimal places" method also if I looked hard enough.

Thus, it seems to me that my solution probably isn't 100% correct for all cases and wouldn't be unless it was computed in decimal. This issue may therefore affect solutions in any language that use FP arithmetic.


Batch, 660 652 bytes

@echo off
set m=%1.
set s=
if %m:~,1%==- set s=-&set m=%m:~1%
:m
if %m:~,1%==0 set m=%m:~1%&goto m
set d=%m:.=%
:d
if %d:~,1%==0 set d=%d:~1%&goto d
for /l %%i in (1,1,%2) do call set d=%%d%%0
call set r=%%d:~%2,1%%
call set d=%%d:~,%2%%
if %r% leq 4 goto r
set r=
:i
set/ai=1+%d:~-1%
set r=%i:~-1%%r%
set d=%d:~,-1%
if %i% leq 9 set d=%d%%r%&goto r
if not "%d%"=="" goto i
set d=1%r:~1%
set m=1%m%
set m=%m:1.0=.%
:r
if %m:~,2%==.0 set m=%m:.0=.%&set d=0%d%&goto r
set i=0
set p=.
:l
if %m:~,1%==. echo %s%%i%%p%%d%&exit/b
if %i%==0 set i=
if "%d%"=="" set d=0&set p=
set i=%i%%d:~,1%
set d=%d:~1%
set m=%m:~1%
goto l

Explanation: Starts by suffixing a . to the parameter in case it doesn't have one already, then trims off the sign (which is saved) and any leading zeros. The resulting variable m is saved for later because it will tell us the desired magnitude of the result. Any .s are then removed, which could result in further leading zeros, so they are removed too. n zeros are suffixed to ensure that there are enough digits to round, then the nth and first n digits are extracted. If the nth digit is not 4 or less then we tediously add 1 to the string. If the string overflows then we increase the magnitude by prefixing a 1, but if it was originally less than 0.1 we do that by removing the 1 we just added and also a 0 after the decimal point. If the magnitude is still less than 1 then we copy the zeros after the decimal point to the result, however if it is 1 or more then we extract the integer portion of the answer, adding extra zeros if necessary to reach the decimal point (which is then deleted as it would show incorrect precision). Finally the sign, integer portion, decimal point and decimal portion are concatenated.

Tags:

Math

Code Golf