【发布时间】:2011-11-24 19:38:17
【问题描述】:
有人知道我如何将双精度值四舍五入到 3 个有效数字,就像本网站上的示例一样
【问题讨论】:
-
不是重复的。提到的问题是关于
number of digits after decimal point。与significant figures的概念不同。
有人知道我如何将双精度值四舍五入到 3 个有效数字,就像本网站上的示例一样
【问题讨论】:
number of digits after decimal point。与significant figures 的概念不同。
我通常不会对 number 本身进行四舍五入,而是在我需要显示数字的字符串表示时对其进行四舍五入,因为通常重要的是显示,需要四舍五入(尽管这可能不是在某些情况下是正确的,也许是你的,但如果是这样,你需要详细说明)。这样,我的号码保持其准确性,但它的显示被简化且更易于阅读。为此,可以使用 DecimalFormat 对象,例如使用“0.000”字符串 (new DecimalFormat("0.000")) 进行初始化,或使用 String.format("%.3f", myDouble),或其他几种方式。
例如:
// yeah, I know this is just Math.PI.
double myDouble = 3.141592653589793;
DecimalFormat myFormat = new DecimalFormat("0.000");
String myDoubleString = myFormat.format(myDouble);
System.out.println("My number is: " + myDoubleString);
// or you can use printf which works like String.format:
System.out.printf("My number is: %.3f%n", myDouble);
【讨论】:
double d = ...;
BigDecimal bd = new BigDecimal(d);
bd = bd.round(new MathContext(3));
double rounded = bd.doubleValue();
【讨论】:
如何将 double 值四舍五入为 3 个有效数字
你不能。双打以二进制表示。它们没有要四舍五入的小数位。获得特定小数位数的唯一方法是将其转换为小数基数并将其保留在那里。将其转换回双精度的那一刻,您又失去了小数精度。 p>
对于这里和其他地方的所有粉丝,转换为其他基数并返回,或乘以和除以十的幂,请显示得到的双精度值 % 0.001 或任何所需的精度,并解释结果。
编辑:具体来说,这些技术的支持者需要解释以下代码的 92% 失败率:
public class RoundingCounterExample
{
static float roundOff(float x, int position)
{
float a = x;
double temp = Math.pow(10.0, position);
a *= temp;
a = Math.round(a);
return (a / (float)temp);
}
public static void main(String[] args)
{
float a = roundOff(0.0009434f,3);
System.out.println("a="+a+" (a % .0001)="+(a % 0.001));
int count = 0, errors = 0;
for (double x = 0.0; x < 1; x += 0.0001)
{
count++;
double d = x;
int scale = 2;
double factor = Math.pow(10, scale);
d = Math.round(d * factor) / factor;
if ((d % 0.01) != 0.0)
{
System.out.println(d + " " + (d % 0.01));
errors++;
}
}
System.out.println(count + " trials " + errors + " errors");
}
}
【讨论】:
public String toSignificantFiguresString(BigDecimal bd, int significantFigures ){
String test = String.format("%."+significantFigures+"G", bd);
if (test.contains("E+")){
test = String.format(Locale.US, "%.0f", Double.valueOf(String.format("%."+significantFigures+"G", bd)));
}
return test;
}
【讨论】:
如果你想手工做:
import java.lang.Math;
public class SigDig {
public static void main(String[] args) {
System.out.println(" -123.456 rounded up to 2 sig figures is " + sigDigRounder(-123.456, 2, 1));
System.out.println(" -0.03394 rounded down to 3 sig figures is " + sigDigRounder(-0.03394, 3, -1));
System.out.println(" 474 rounded up to 2 sig figures is " + sigDigRounder(474, 2, 1));
System.out.println("3004001 rounded down to 4 sig figures is " + sigDigRounder(3004001, 4, -1));
}
public static double sigDigRounder(double value, int nSigDig, int dir) {
double intermediate = value/Math.pow(10,Math.floor(Math.log10(Math.abs(value)))-(nSigDig-1));
if(dir > 0) intermediate = Math.ceil(intermediate);
else if (dir< 0) intermediate = Math.floor(intermediate);
else intermediate = Math.round(intermediate);
double result = intermediate * Math.pow(10,Math.floor(Math.log10(Math.abs(value)))-(nSigDig-1));
return(result);
}
}
上述方法将双精度数舍入为所需的有效数字数,处理负数,并且可以明确告知向上或向下舍入
【讨论】:
Sean Owen (https://stackoverflow.com/a/7548871/274677) 给出的答案没有错。但是,根据您的用例,您可能希望得到一个字符串表示。在这种情况下,IMO 最好在 BigDecimal 空间中使用以下方法进行转换:
bd.toPlainString();
...如果这是您的用例,那么您可能会对根据 Owen 的回答改编的代码将产生以下结果感到沮丧:
d = 0.99, significantDigits = 3 ==> 0.99
...而不是更准确的0.990。
如果这些事情在您的用例中很重要,那么我建议对欧文的回答进行以下修改;您显然也可以返回 BigDecimal 本身,而不是调用 toPlainString() — 我只是为了完整性而提供这种方式。
public static String setSignificanDigits(double value, int significantDigits) {
if (significantDigits < 0) throw new IllegalArgumentException();
// this is more precise than simply doing "new BigDecimal(value);"
BigDecimal bd = new BigDecimal(value, MathContext.DECIMAL64);
bd = bd.round(new MathContext(significantDigits, RoundingMode.HALF_UP));
final int precision = bd.precision();
if (precision < significantDigits)
bd = bd.setScale(bd.scale() + (significantDigits-precision));
return bd.toPlainString();
}
【讨论】:
双 d=3.142568; System.out.printf("答案:%.3f", d);
【讨论】: