你怎么能将任何数字(不只是整数> 0)舍入到N位有效数字?
例如,如果我想要舍入到三位有效数字,我正在寻找一个可以采用的公式:
1,239,451并返回1,240,000
12.1257并返回12.1
.0681并返回.0681
5并返回5
当然,算法不应该被硬编码为仅处理3的N,尽管这将是一个开始.
这里是Java中的相同代码,没有其他答案所具有的12.100000000000001错误
我还删除了重复的代码,更改power
为类型整数以防止浮动问题n - d
完成,并使长中间更清晰
该错误是由一个较大的数字乘以一个小数字引起的.相反,我划分两个相似大小的数字.
编辑
修正了更多错误.添加了0的检查,因为它会导致NaN.使该函数实际上使用负数(原始代码不处理负数,因为负数的对数是一个复数)
public static double roundToSignificantFigures(double num, int n) { if(num == 0) { return 0; } final double d = Math.ceil(Math.log10(num < 0 ? -num: num)); final int power = n - (int) d; final double magnitude = Math.pow(10, power); final long shifted = Math.round(num*magnitude); return shifted/magnitude; }
摘要:
double roundit(double num, double N) { double d = log10(num); double power; if (num > 0) { d = ceil(d); power = -(d-N); } else { d = floor(d); power = -(d-N); } return (int)(num * pow(10.0, power) + 0.5) * pow(10.0, -power); }
因此,您需要找到第一个非零数字的小数位,然后保存下一个N-1个数字,然后根据其余数字舍入第N个数字.
我们可以使用log来做第一个.
log 1239451 = 6.09 log 12.1257 = 1.08 log 0.0681 = -1.16
所以对于数字> 0,取日志的ceil.对于数字<0,请记录日志.
现在我们有数字d
:第一种情况下为7,第二种情况下为2,第3种情况下为-2.
我们必须绕过这个(d-N)
数字.就像是:
double roundedrest = num * pow(10, -(d-N)); pow(1239451, -4) = 123.9451 pow(12.1257, 1) = 121.257 pow(0.0681, 4) = 681
那么做标准的舍入事情:
roundedrest = (int)(roundedrest + 0.5);
并撤消战俘.
roundednum = pow(roundedrest, -(power))
其中功率是上面计算的功率.
关于准确性:Pyrolistical的答案确实更接近实际结果.但请注意,在任何情况下都不能完全代表12.1.如果您打印答案如下:
System.out.println(new BigDecimal(n));
答案是:
Pyro's: 12.0999999999999996447286321199499070644378662109375 Mine: 12.10000000000000142108547152020037174224853515625 Printing 12.1 directly: 12.0999999999999996447286321199499070644378662109375
所以,使用Pyro的答案!
这是一个简短而又甜蜜的JavaScript实现:
function sigFigs(n, sig) { var mult = Math.pow(10, sig - Math.floor(Math.log(n) / Math.LN10) - 1); return Math.round(n * mult) / mult; } alert(sigFigs(1234567, 3)); // Gives 1230000 alert(sigFigs(0.06805, 3)); // Gives 0.0681 alert(sigFigs(5, 3)); // Gives 5
不是"短而甜蜜"的JavaScript实现
Number(n).toPrecision(sig)
例如
alert(Number(12345).toPrecision(3)
?
对不起,我在这里不是很滑稽,只是使用Claudiu的"roundit"函数和JavaScript中的.toPrecision给出了不同的结果,但只是在最后一位数的四舍五入.
JavaScript的:
Number(8.14301).toPrecision(4) == 8.143
.净
roundit(8.14301,4) == 8.144
Pyrolistical(非常好!)解决方案仍有问题.Java中的最大double值大约为10 ^ 308,而最小值大约为10 ^ -324.因此,将函数roundToSignificantFigures
应用于十分之一的幂时,可能会遇到麻烦Double.MIN_VALUE
.例如,当你打电话时
roundToSignificantFigures(1.234E-310, 3);
然后变量power
将具有值3 - (-309)= 312.因此,变量magnitude
将变为Infinity
,从那时起它就是垃圾.幸运的是,这不是一个不可逾越的问题:它只是溢出的因素 magnitude
.真正重要的是产品 num * magnitude
,而且不会溢出.解决这个问题的一种方法是将因子乘以magintude
两个步骤:
public static double roundToNumberOfSignificantDigits(double num, int n) {
final double maxPowerOfTen = Math.floor(Math.log10(Double.MAX_VALUE));
if(num == 0) {
return 0;
}
final double d = Math.ceil(Math.log10(num < 0 ? -num: num));
final int power = n - (int) d;
double firstMagnitudeFactor = 1.0;
double secondMagnitudeFactor = 1.0;
if (power > maxPowerOfTen) {
firstMagnitudeFactor = Math.pow(10.0, maxPowerOfTen);
secondMagnitudeFactor = Math.pow(10.0, (double) power - maxPowerOfTen);
} else {
firstMagnitudeFactor = Math.pow(10.0, (double) power);
}
double toBeRounded = num * firstMagnitudeFactor;
toBeRounded *= secondMagnitudeFactor;
final long shifted = Math.round(toBeRounded);
double rounded = ((double) shifted) / firstMagnitudeFactor;
rounded /= secondMagnitudeFactor;
return rounded;
}
这个java解决方案怎么样:
double roundToSignificantFigure(double num, int precision){ return new BigDecimal(num) .round(new MathContext(precision, RoundingMode.HALF_EVEN)) .doubleValue(); }