【问题标题】:Java rounding up to an int using Math.ceilJava 使用 Math.ceil 将整数四舍五入
【发布时间】:2011-08-21 16:02:10
【问题描述】:
int total = (int) Math.ceil(157/32);

为什么它仍然返回 4? 157/32 = 4.90625,我需要总结一下,我环顾四周,这似乎是正确的方法。

我尝试将 total 用作 double 类型,但得到 4.0。

我做错了什么?

【问题讨论】:

    标签: java math


    【解决方案1】:

    你正在做157/32 将两个整数相除,这总是导致向下取整的整数。因此(int) Math.ceil(...) 没有做任何事情。有三种可能的解决方案可以实现您想要的。我建议使用选项 1选项 2。请使用选项0

    选项 0

    ab 转换为双精度,您可以使用除法和Math.ceil,因为您希望它可以工作。但是我强烈反对使用这种方法,因为双除法可能不精确。要了解有关双精度不精确的更多信息,请参阅this question

    int n = (int) Math.ceil((double) a / b));
    

    选项 1

    int n = a / b + ((a % b == 0) ? 0 : 1); 
    

    如果ab 都是整数,那么你总是使用 floor 来处理a / b。然后你有一个内联 if 语句女巫检查你是否应该 ceil 而不是 floor。所以+1或+0,如果除法有余数,你需要+1。 a % b == 0 检查余数。

    选项 2

    这个选项很短,但可能不太直观。我认为这种不太直观的方法会比双除法和比较方法更快:
    请注意,这不适用于b < 0

    int n = (a + b - 1) / b;
    

    为了减少溢出的机会,您可以使用以下方法。但是请注意,它不适用于a = 0b < 1

    int n = (a - 1) / b + 1;
    

    “不太直观的方法”背后的解释

    由于在 Java(以及大多数其他编程语言)中将两个整数相除,结果总是会取底。所以:

    int a, b;
    int result = a/b (is the same as floor(a/b) )
    

    但我们不想要floor(a/b),而是ceil(a/b),并使用Wikipedia中的定义和绘图:

    通过这些 floor 和 ceil 函数的图,您可以看到关系。

    你可以看到floor(x) <= ceil(x)。我们需要floor(x + s) = ceil(x)。所以我们需要找到s。如果我们采用1/2 <= s < 1,那将是正确的(尝试一些数字,你会看到它确实如此,我自己很难证明这一点)。还有1/2 <= (b-1) / b < 1,所以

    ceil(a/b) = floor(a/b + s)
              = floor(a/b + (b-1)/b)
              = floor( (a+b-1)/b) )
    

    这不是一个真实的证明,但我希望您对此感到满意。如果有人可以更好地解释它,我也会很感激。可以在MathOverflow 上询问。

    【讨论】:

    • 如果您能解释不太直观的方法背后的直觉,那将是一个巨大的帮助?我知道这是正确的,我想知道你是怎么做到的,我怎样才能在数学上证明它是正确的。我试着用数学方法解决它,不相信。
    • 我希望你对我的编辑感到满意,我认为我不能做得更好:(
    • 我假设 Math.floor 和 ceil 仅适用于整数除法,而不适用于将值转换为双精度数时的长除法。反例是 4611686018427386880 / 4611686018427387137 在地板上失败和 4611686018427386881 / 4611686018427386880 在天花板上失败
    • 澄清一点:选项2的两个子选项的结果并非在所有情况下都相同。 a 的值为零将在第一个中提供 0,在第二个中提供 1(这不是大多数应用程序的正确答案)。
    • 你确定你的意思不是“但是请注意它不适用于 a = 0 和 b 1”
    【解决方案2】:

    157/32 是 int/int,结果为 int

    尝试使用双字面值 - 157/32d,即int/double,结果为double

    【讨论】:

    【解决方案3】:

    157/32 是一个整数除法,因为所有数字文字都是整数,除非另外指定后缀(d 表示双精度,l 表示长)

    除法被四舍五入(到 4)之前,它被转换为双精度(4.0),然后向上舍入(到 4.0)

    如果你使用变量,你可以避免这种情况

    double a1=157;
    double a2=32;
    int total = (int) Math.ceil(a1/a2);
    

    【讨论】:

      【解决方案4】:
      int total = (int) Math.ceil((double)157/32);
      

      【讨论】:

        【解决方案5】:

        没有人提到最直观的:

        int x = (int) Math.round(Math.ceil((double) 157 / 32));
        

        此解决方案修复了 double 除法不精确。

        【讨论】:

        • Math.round 返回 long
        • 感谢@ZulqurnainJutt,添加了演员表
        【解决方案6】:

        在 Java 中添加 .0 将使其成为双倍...

        int total = (int) Math.ceil(157.0 / 32.0);
        

        【讨论】:

          【解决方案7】:

          两个整数相除时,例如,

          int c = (int) a / (int) b;

          结果是int,其值是a 除以b,四舍五入到零。因为结果已经四舍五入,ceil() 不做任何事情。请注意,此舍入与floor() 不同,后者向负无穷大舍入。所以,3/2 等于 1(并且 floor(1.5) 等于 1.0,但 (-3)/2 等于 -1(但 floor(-1.5) 等于 -2.0)。

          这很重要,因为如果a/b 始终与floor(a / (double) b) 相同,那么您可以将a/bceil() 实现为-( (-a) / b)

          ceil(a/b)获取的建议

          int n = (a + b - 1) / b;,相当于a / b + (b - 1) / b,或者(a - 1) / b + 1

          之所以有效,是因为ceil(a/b) 总是比floor(a/b) 大一,除非a/b 是一个整数。所以,你想把它撞到(或过去)下一个整数,除非a/b 是一个整数。添加1 - 1 / b 将执行此操作。对于整数,它不会将它们推高到下一个整数。对于其他一切,它都会。

          哎呀。希望这是有道理的。我确信有一种更优雅的数学方式来解释它。

          【讨论】:

            【解决方案8】:

            还可以将数字从整数转换为实数,您可以添加一个点:

            int total = (int) Math.ceil(157/32.);
            

            并且 (157/32.) 的结果也将是真实的。 ;)

            【讨论】:

              【解决方案9】:
              int total = (int) Math.ceil( (double)157/ (double) 32);
              

              【讨论】:

                【解决方案10】:

                检查以下解决方案以了解您的问题:

                int total = (int) Math.ceil(157/32);
                

                这里你应该将分子乘以 1.0,然后它就会给出你的答案。

                int total = (int) Math.ceil(157*1.0/32);
                

                【讨论】:

                  【解决方案11】:

                  使用 double 来投射像

                  Math.ceil((double)value) 或喜欢

                  Math.ceil((double)value1/(double)value2);
                  

                  【讨论】:

                    【解决方案12】:

                    Java 默认只提供楼层划分/。但是我们可以用地板来写天花板。让我们看看:

                    任何整数y 都可以写成y == q*k+r。根据楼层划分的定义(这里是floor),对r进行四舍五入,

                    floor(q*k+r, k) == q  , where 0 ≤ r ≤ k-1
                    

                    和天花板分割(这里是ceil),它四舍五入r₁

                    ceil(q*k+r₁, k) == q+1  , where 1 ≤ r₁ ≤ k
                    

                    我们可以用r+1 代替r₁

                    ceil(q*k+r+1, k) == q+1  , where 0 ≤ r ≤ k-1
                    


                    然后我们将第一个等式代入第三个等式,得到qget

                    ceil(q*k+r+1, k) == floor(q*k+r, k) + 1  , where 0 ≤ r ≤ k-1
                    

                    最后,给定任何整数y,其中y = q*k+r+1 对应一些q,k,r,我们有

                    ceil(y, k) == floor(y-1, k) + 1
                    

                    我们完成了。希望这会有所帮助。

                    【讨论】:

                    • 我确信这是正确的,但由于这样做的目的是为了澄清,我不清楚为什么 ceil 是从直观定义中定义的,特别是我们正在采取的地方整数的ceil,即r1 = k。由于边缘情况是这方面的棘手之处,我认为需要进一步说明。
                    • @LuigiPlinge 对我来说,由于除法运算上下文中地板和天花板之间的内在差异,推导不能更简单。我认为您不需要关注边缘情况 - 当您尝试通过分解整数来统一地板和天花板的定义时,这是很自然的事实。结果证明只是三步,结论大致可以记为“后退一步,再前一步”。
                    【解决方案13】:

                    有两种方法可以将双精度值四舍五入。

                    1. Math.ceil
                    2. Math.floor

                    如果您希望您的答案 4.90625 为 4,那么您应该使用 Math.floor,如果您希望您的答案 4.90625 为 5,那么您可以使用 Math.ceil

                    您可以参考以下代码。

                    public class TestClass {
                    
                        public static void main(String[] args) {
                            int floorValue = (int) Math.floor((double)157 / 32);
                            int ceilValue = (int) Math.ceil((double)157 / 32);
                            System.out.println("Floor: "+floorValue);
                            System.out.println("Ceil: "+ceilValue);
                    
                        }
                    
                    }
                    

                    【讨论】:

                      【解决方案14】:

                      我知道这是一个老问题,但在我看来,我们有一个更好的方法是使用 BigDecimal 来避免精度损失。顺便说一句,使用此解决方案,我们可以使用多种舍入和缩放策略。

                      final var dividend = BigDecimal.valueOf(157);
                      final var divisor = BigDecimal.valueOf(32);
                      final var result = dividend.divide(divisor, RoundingMode.CEILING).intValue();
                      

                      【讨论】:

                        【解决方案15】:
                        int total = (157-1)/32 + 1
                        

                        或更笼统的

                        (a-1)/b +1 
                        

                        【讨论】:

                        • 我认为这行得通,但您并没有真正解释为什么原始版本行不通。
                        • "但请注意,它不适用于 a = 0 和 b "
                        猜你喜欢
                        • 1970-01-01
                        • 2011-03-11
                        • 1970-01-01
                        • 1970-01-01
                        • 2014-08-26
                        • 2015-01-26
                        • 1970-01-01
                        • 1970-01-01
                        • 2017-12-07
                        相关资源
                        最近更新 更多