【问题标题】:Round a double to 2 decimal places [duplicate]将 double 舍入到小数点后 2 位 [重复]
【发布时间】:2011-02-18 00:12:16
【问题描述】:

如果值为200.3456,则应格式化为200.34。 如果是200,那么应该是200.00

【问题讨论】:

  • 正如 Monn 评论的那样(在回答中),你真的想要 200.34 还是 200.35 来换取 200.3456?当您接受我的回答时,我猜您确实想要四舍五入(+ 也可能是格式化),而不仅仅是截断。但您能否澄清一下您的意思?
  • 显然不是你的问题的答案,但任何阅读这个问题的人都应该认真考虑为什么他们真的需要使用 Double 而不是 BigDecimal。
  • @BillK 我会假设因为 BigDecimal 需要 BigPerformanceHit。
  • 这不是重复的。另一个问题需要一个字符串作为结果。这个想要一个双倍的解决方案是不同的。
  • 不是重复的;格式和舍入是两个完全不同的东西。

标签: java double rounding


【解决方案1】:

这是一个实用程序,它四舍五入(而不是截断)一个双精度数到指定的小数位数。

例如:

round(200.3456, 2); // returns 200.35

原版;小心这个

public static double round(double value, int places) {
    if (places < 0) throw new IllegalArgumentException();

    long factor = (long) Math.pow(10, places);
    value = value * factor;
    long tmp = Math.round(value);
    return (double) tmp / factor;
}

在小数位数非常多(例如round(1000.0d, 17))或大整数部分(例如round(90080070060.1d, 9))的极端情况下,这严重崩溃。感谢Sloin 指出这一点。

多年来,我一直在使用上述方法将“不太大”的双精度数四舍五入到小数点后 2 或 3 位(例如,为了记录目的,以秒为单位清理时间:27.987654321987 -> 27.99)。但我想最好避免它,因为更可靠的方法很容易获得,而且代码也更简洁。

所以,改用这个

(改编自this answer by Louis Wassermanthis one by Sean Owen。)

public static double round(double value, int places) {
    if (places < 0) throw new IllegalArgumentException();

    BigDecimal bd = BigDecimal.valueOf(value);
    bd = bd.setScale(places, RoundingMode.HALF_UP);
    return bd.doubleValue();
}

请注意,HALF_UP 是“学校常教”的舍入模式。如果您怀疑自己需要其他内容,例如 Bankers’ Rounding,请仔细阅读 RoundingMode documentation

当然,如果您愿意,也可以将上述内容内联成一个单行:
new BigDecimal(value).setScale(places, RoundingMode.HALF_UP).doubleValue()

在任何情况下

永远记住,使用floatdouble 的浮点表示是不精确的。 例如,考虑以下表达式:

999199.1231231235 == 999199.1231231236 // true
1.03 - 0.41 // 0.6200000000000001

For exactness, you want to use BigDecimal。在此过程中,请使用带有 String 的构造函数,而不是使用 double 的构造函数。例如,尝试执行这个:

System.out.println(new BigDecimal(1.03).subtract(new BigDecimal(0.41)));
System.out.println(new BigDecimal("1.03").subtract(new BigDecimal("0.41")));

有关该主题的一些出色的进一步阅读:


如果您想要 String formatting 而不是(或除此之外)严格舍入数字,请参阅其他答案。

具体来说,请注意round(200, 0) 返回200.0。如果你想输出“200.00”,你应该先四舍五入然后格式化输出结果(这在Jesper's answer中有完美的解释)。

【讨论】:

  • 再一次,投反对票对评论更有用。 (请注意,问题 模棱两可,我的回答清楚地表明它并没有解决问题的所有解释。)
  • 查看answer 了解为什么tmp/factor 之类的事情可能会失败
  • 也阅读关于该答案的第一条评论。显然,如果您正在处理精确的(例如货币)值,您should not be using double in the first place。 (在这种情况下,use BigDecimal。)
  • 谢谢,这看起来坚如磐石,顺便说一句更优雅:return new BigDecimal(value).setScale(places, BigDecimal.ROUND_HALF_UP).doubleValue();
  • @lisak:在什么情况下四舍五入到 18 位才有意义? rounding 的目的不是创建一个简化的表示吗?好的,也许在计算大笔贷款的利息时.. 但希望任何进行财务计算的人都知道使用BigDecimal 而不是double 来表示他们的价值,所以不会寻找这个解决方案。滥用一个方法,然后称它为shitty,是不专业的。不过,最好强调解决方案的局限性。
【解决方案2】:

如果您只想打印小数点后两位数的double,请使用以下内容:

double value = 200.3456;
System.out.printf("Value: %.2f", value);

如果您希望将结果显示在 String 而不是打印到控制台,请使用 String.format() 和相同的参数:

String result = String.format("%.2f", value);

或者使用类DecimalFormat:

DecimalFormat df = new DecimalFormat("####0.00");
System.out.println("Value: " + df.format(value));

【讨论】:

  • DecimalFormat还可以选择舍入模式;默认值将匹配 OP 的示例,或者他们可能想要RoundingMode.DOWN
  • 前两个选项为大于 -0.005 且小于 0 的数字提供 -0.00。如果您在报告中显示该值,这可能不是最佳选择。
  • @Voicu 对于您的具体情况,我认为您应该使用 3 位或更多小数位。这取决于您的误差幅度是多少。
  • 这个答案是 真正的 答案...其他的都是双精度的,这是完全不同的格式化操作。
  • 如果你想把结果放在双变量而不是字符串中怎么办?
【解决方案3】:

我认为这更容易:

double time = 200.3456;
DecimalFormat df = new DecimalFormat("#.##");      
time = Double.valueOf(df.format(time));

System.out.println(time); // 200.35

请注意,这实际上会为您进行舍入,而不仅仅是格式化。

【讨论】:

  • 请注意,如果用户语言环境不是 US-en 或类似语言,这将中断。例如,在西班牙语中,格式为“#,##”。请改用 Jonik 的答案。
  • 这是错误的 String.format("%.2f",$F{accrued_interest})
  • @Jhovanny 你不能像这样声明一个 double time = 200,3456 with throw errors...
【解决方案4】:

最简单的方法是做这样的把戏;

double val = ....;
val = val*100;
val = Math.round(val);
val = val /100;

如果 val 从 200.3456 开始,那么它会到 20034.56,然后它会四舍五入到 20035,然后我们将它除以得到 200.34。

如果你想总是向下取整,我们总是可以通过转换为 int 来截断:

double val = ....;
val = val*100;
val = (double)((int) val);
val = val /100;

这种技术适用于大多数情况,因为对于非常大的双精度数(正数或负数)它可能会溢出。但如果您知道您的值将在适当的范围内,那么这应该适合您。

【讨论】:

  • 好简单的答案。我只想补充一点,Math.Round 应该是Math.round。来自Math.Round(val); 的结果应该是一个双精度型,因为它通常返回一个长型:val = (double) Math.round(val);
  • 完美答案,因为值的结果将在其他答案中使用 comma 计算。例如,双精度值是 3.72 如果我使用 format() 函数,新的双精度值会更改 3,72 如果我想将此新值设置为双精度属性,它将抛出 NumberFormatException 异常:对于输入字符串:“3,72”。但是你得到了这个逻辑操作,而不是函数。最好的问候。
  • 接受答案的round方法。
【解决方案5】:

请使用Apache commons math:

Precision.round(10.4567, 2)

【讨论】:

  • 这实际上并没有在像 Java 这样的按值传递语言中做任何事情,更不用说 OP 要求的了。
  • @EJP 你为什么这么挑剔?所要做的就是将返回值分配给一个变量...
  • 请注意,在内部,Precision.round 只是将 Double 转换为 BigDecimal 并再次转换回来,就像在 Jonik 的解决方案中一样。
  • 我认为这是一个非常干净的解决方案,即使它让你依赖于一个库。
  • @HendyIrawan 它不符合 OP 的要求。它不能。将结果存储在浮点数中的任何解决方案都不可能奏效。请参阅我在副本中的答案以了解原因。
【解决方案6】:
function Double round2(Double val) {
    return new BigDecimal(val.toString()).setScale(2,RoundingMode.HALF_UP).doubleValue();
}

注意 toString()!!!!

这是因为 BigDecimal 转换了 double 的精确二进制形式!!!

这些是各种建议的方法及其失败案例。

// Always Good!
new BigDecimal(val.toString()).setScale(2,RoundingMode.HALF_UP).doubleValue() 

Double val = 260.775d; //EXPECTED 260.78
260.77 - WRONG - new BigDecimal(val).setScale(2,RoundingMode.HALF_UP).doubleValue()

Double val = 260.775d; //EXPECTED 260.78
260.77 - TRY AGAIN - Math.round(val * 100.d) / 100.0d  

Double val = 256.025d; //EXPECTED 256.03d
256.02 - OOPS - new DecimalFormat("0.00").format(val) 
// By default use half even, works if you change mode to half_up 

Double val = 256.025d; //EXPECTED 256.03d
256.02 - FAIL - (int)(val * 100 + 0.5) / 100.0;

【讨论】:

  • 应该是被接受的答案,因为与你干净易读的答案相比,接受的答案是如此混乱
  • 我刚刚将BigDecimal(val.toString()) 更改为BigDecimal(Double.toString(value)) 以避免在您使用double 而不是Double 时 val.toString() 出现错误。
【解决方案7】:
double value= 200.3456;
DecimalFormat df = new DecimalFormat("0.00");      
System.out.println(df.format(value));

【讨论】:

    【解决方案8】:

    如果你真的想要相同的双精度,但以你想要的方式四舍五入,你可以使用 BigDecimal,例如

    new BigDecimal(myValue).setScale(2, RoundingMode.HALF_UP).doubleValue();
    

    【讨论】:

    • 它是否也适用于负数?
    • 完美! (它适用于负数)
    【解决方案9】:
    double d = 28786.079999999998;
    String str = String.format("%1.2f", d);
    d = Double.valueOf(str);
    

    【讨论】:

      【解决方案10】:

      对于两位四舍五入的数字。非常简单,您基本上是在更新变量,而不仅仅是 DecimalFormat 所做的显示目的。

      x = Math.floor(x * 100) / 100;

      【讨论】:

        【解决方案11】:

        四舍五入通常不是人们想要的。而是使用String.format() 以所需的格式表示它。

        【讨论】:

        • 我需要舍入一个 double 以将其用作进一步计算中的粒度级别
        • @odiszapc:那么你使用了错误的数据类型。如果您希望能够指定任意精度,则需要使用任意精度类型。
        • 我从 DB 获得双打,它是原生 DB 类型。但后来我特别舍入它以使用舍入值作为构建矩阵的关键。当然这个key是用String.format方法得到的格式化字符串。
        【解决方案12】:

        在您的问题中,您似乎也想避免对数字进行四舍五入?我认为 .format() 将使用半数对数字进行四舍五入,afaik?
        所以如果你想四舍五入,200.3456 应该是 200.35,精度为 2。但在你的情况下,如果你只想要前 2 个然后丢弃其余的?

        您可以将它乘以 100,然后转换为 int(或取数字的底),然后再除以 100。

        200.3456 * 100 = 20034.56;  
        (int) 20034.56 = 20034;  
        20034/100.0 = 200.34;
        

        不过,您可能会遇到非常大的数字接近边界的问题。在这种情况下,转换为字符串和子字符串将同样容易。

        【讨论】:

        • 感谢您的回答。如果值为 200.3456,则工作正常,但如果值为 200,则应为 200.00
        【解决方案13】:
        value = (int)(value * 100 + 0.5) / 100.0;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-11-16
          • 2015-11-24
          • 2012-03-11
          • 1970-01-01
          • 2018-01-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多