舍入到任意数量的有效数字
你怎么能把任何数字(不只是整数> 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; }
你是否试过用手工方式编码?
- 将数字转换为string
- 从string的开头开始,计数数字 – 前导零不是重要的,其他的都是。
- 当你到达“第n位”时,向下看下一位数字,如果它是5或更高,则向上取整。
- 用零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) );
Number
function会将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()); }
此代码使用内置的格式化函数,该函数转换为舍入函数