舍入到任意数量的有效数字

你怎么能把任何数字(不只是整数> 0)N个有效数字?

例如,如果我想舍入三位有效数字,我正在寻找一个公式,可以采取:

1,239,451并返还124万

12.1257并​​返回12.1

.0681并返回.0681

5并返回5

当然,algorithm不应该被硬编码,只能处理N的3,尽pipe这将是一个开始。

这是Java中没有12.100000000000001其他答案有相同的代码

我也删除了重复的代码,改变了一个types整数的power ,以防止浮动问题,当n - d完成,并使长中间更清晰

这个错误是由于大数乘以一个小数字造成的。 相反,我划分了两个相似大小的数字。

编辑
修复了更多的错误。 增加了对0的检查,因为它会导致NaN。 使function实际上与负数(原始代码不处理负数,因为负数的日志是一个复数)工作,

 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; } 

这是一个简短而甜蜜的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 

概要:

 double roundit(double num, double N) { double d = log10(num); double power; if (num > 0) { d = ceil(d); power = -(dN); } else { d = floor(d); power = -(dN); } return (int)(num * pow(10.0, power) + 0.5) * pow(10.0, -power); } 

因此,您需要find第一个非零数字的小数位,然后保存下一个N-1数字,然后根据其余数字四舍五入取整。

我们可以用日志做第一个。

 log 1239451 = 6.09 log 12.1257 = 1.08 log 0.0681 = -1.16 

所以对于数字> 0,采取日志的细胞。 对于数字<0,采取日志的地板。

现在我们在第一个例子中有d :7,第二个是2,第三个是-2。

我们必须围绕第(dN)个数字。 就像是:

 double roundedrest = num * pow(10, -(dN)); 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实现

 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 

这个Java解决scheme如何:

 double roundToSignificantFigure(double num,int precision){
 返回新的BigDecimal(num)
             .round(新的MathContext(精度,RoundingMode.HALF_EVEN))
             .doubleValue(); 
 }

Pyrolistical的(非常好的)解决scheme仍然有一个问题。 Java中的最大值是10 ^ 308,最小值是10 ^ -324。 因此,在将函数roundToSignificantFigures应用于Double.MIN_VALUE的十几个幂数内的情况下,可能会遇到麻烦。 例如,当你打电话

 roundToSignificantFigures(1.234E-310, 3); 

那么variables的值就是3 – ( – 309)= 312.因此,variables的magnitude就变成了Infinity ,这些都是从那里开始的垃圾。 幸运的是,这不是一个不可逾越的问题:它只是溢出的因素 。 真正重要的是产品 num * magnitude ,而且不会溢出。 解决这个问题的一个方法是把因子magintude的乘法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; }
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; } 

这是Ates的JavaScript处理负数的修改版本。

 function sigFigs(n, sig) { if ( n === 0 ) return 0 var mult = Math.pow(10, sig - Math.floor(Math.log(n < 0 ? -n: n) / Math.LN10) - 1); return Math.round(n * mult) / mult; } 

你是否试过用手工方式编码?

  1. 将数字转换为string
  2. 从string的开头开始,计数数字 – 前导零不是重要的,其他的都是。
  3. 当你到达“第n位”时,向下看下一位数字,如果它是5或更高,则向上取整。
  4. 用零replace所有的尾数字。

[更正,2009-10-26]

实际上,对于N个重要的小数位:

•乘以10 N
•加0.5
•截断小数位(即,将结果截断为一个整数)
•除以10 N

对于N个重要的整数 (非小数)数字:

•将数字除以10 N
•加0.5
•截断小数位(即,将结果截断为一个整数)
乘以10 N

您可以在任何计算器上执行此操作,例如,具有“INT”(整数截断)运算符。

 /** * Set Significant Digits. * @param value value * @param digits digits * @return */ public static BigDecimal setSignificantDigits(BigDecimal value, int digits) { //# Start with the leftmost non-zero digit (eg the "1" in 1200, or the "2" in 0.0256). //# Keep n digits. Replace the rest with zeros. //# Round up by one if appropriate. int p = value.precision(); int s = value.scale(); if (p < digits) { value = value.setScale(s + digits - p); //, RoundingMode.HALF_UP } value = value.movePointRight(s).movePointLeft(p - digits).setScale(0, RoundingMode.HALF_UP) .movePointRight(p - digits).movePointLeft(s); s = (s > (p - digits)) ? (s - (p - digits)) : 0; return value.setScale(s); } 

这里是Pyrolistical的(当前最好的答案)在Visual Basic.NET中的代码,任何人都需要它:

 Public Shared Function roundToSignificantDigits(ByVal num As Double, ByVal n As Integer) As Double If (num = 0) Then Return 0 End If Dim d As Double = Math.Ceiling(Math.Log10(If(num < 0, -num, num))) Dim power As Integer = n - CInt(d) Dim magnitude As Double = Math.Pow(10, power) Dim shifted As Double = Math.Round(num * magnitude) Return shifted / magnitude End Function 

这是晚了5年,但我会分享给仍然有同样问题的其他人。 我喜欢它,因为它很简单,没有在代码方面的计算。 请参阅内置方法以显示更多信息的重要数字 。

这是如果你只是想打印出来。

 public String toSignificantFiguresString(BigDecimal bd, int significantFigures){ return String.format("%."+significantFigures+"G", bd); } 

这是如果你想转换它:

 public BigDecimal toSignificantFigures(BigDecimal bd, int significantFigures){ String s = String.format("%."+significantFigures+"G", bd); BigDecimal result = new BigDecimal(s); return result; } 

下面是它的一个例子:

 BigDecimal bd = toSignificantFigures(BigDecimal.valueOf(0.0681), 2); 

JavaScript的:

 Number( my_number.toPrecision(3) ); 

Numberfunction会将forms"8.143e+5"输出更改为"814300"

这是我在VB中想到的一个:

 Function SF(n As Double, SigFigs As Integer) Dim l As Integer = n.ToString.Length n = n / 10 ^ (l - SigFigs) n = Math.Round(n) n = n * 10 ^ (l - SigFigs) Return n End Function 

return new BigDecimal(value, new MathContext(significantFigures, RoundingMode.HALF_UP)).doubleValue();

 public static double roundToSignificantDigits(double num, int n) { return Double.parseDouble(new java.util.Formatter().format("%." + (n - 1) + "e", num).toString()); } 

此代码使用内置的格式化函数,该函数转换为舍入函数