如何将任何数字(不仅是整数> 0)舍入为N个有效数字?
例如,如果我想四舍五入到三个有效数字,那么我正在寻找一个可能采用的公式:
1,239,451,回报1,240,000
12.1257并返回12.1
.0681并返回.0681
5并返回5
自然,不应将算法硬编码为仅处理N的3,尽管这只是一个开始。
Answers:
这是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;
}
这是一个简短而有趣的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
n==0
:)
Math.log(n) / Math.LN10
而不是做Math.log10(n)
吗?
Math.floor(x) == Math.ceil(x) - 1
?因为在什么时候不是x
整数?我认为该pow
函数的第二个参数应该是sig - Math.ceil(Math.log(n) / Math.LN10)
(或仅使用Math.log10
)
概要:
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 1239451 = 6.09
log 12.1257 = 1.08
log 0.0681 = -1.16
因此,对于大于0的数字,请取对数的上限。对于数字<0,请使用日志的底数。
现在我们有数字d
:第一种情况为7,第二种情况为2,第三种情况为-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实现
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
Number(814301).toPrecision(4) == "8.143e+5"
。如果要向用户显示,通常不是您想要的。
Pyrolistical的(非常好!)解决方案仍然存在问题。Java中的最大double值约为10 ^ 308,而最小值约为10 ^ -324。因此,在将函数应用roundToSignificantFigures
到的10的几次幂之内时,可能会遇到麻烦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(双精度整数,整数精度){ 返回新的BigDecimal(num) .round(新的MathContext(precision,RoundingMode.HALF_EVEN)) .doubleValue(); }
这是处理负数的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;
}
这是晚了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);
[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 (e.g. 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);
}
如果有人需要,这是Visual Basic.NET中Pyrolistical的(当前最重要的答案)代码:
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
return new BigDecimal(value, new MathContext(significantFigures, RoundingMode.HALF_UP)).doubleValue();
我在Go中需要此功能,因为Go标准库的缺少math.Round()
(在go1.10之前)有点复杂。因此,我也必须将其提高。这是我对Pyrolistical的出色回答的译文:
// TODO: replace in go1.10 with math.Round()
func round(x float64) float64 {
return float64(int64(x + 0.5))
}
// SignificantDigits rounds a float64 to digits significant digits.
// Translated from Java at https://stackoverflow.com/a/1581007/1068283
func SignificantDigits(x float64, digits int) float64 {
if x == 0 {
return 0
}
power := digits - int(math.Ceil(math.Log10(math.Abs(x))))
magnitude := math.Pow(10, float64(power))
shifted := round(x * magnitude)
return shifted / magnitude
}
您只需使用FloatToStrF,就可以避免使用10等的幂进行所有这些计算。
FloatToStrF允许您(除其他外)在输出值(它将是一个字符串)中选择精度(有效数字数量)。当然,您可以将StrToFloat应用于此值,以将四舍五入后的值作为浮点数。
看这里: