【发布时间】:2011-08-21 16:02:10
【问题描述】:
int total = (int) Math.ceil(157/32);
为什么它仍然返回 4? 157/32 = 4.90625,我需要总结一下,我环顾四周,这似乎是正确的方法。
我尝试将 total 用作 double 类型,但得到 4.0。
我做错了什么?
【问题讨论】:
int total = (int) Math.ceil(157/32);
为什么它仍然返回 4? 157/32 = 4.90625,我需要总结一下,我环顾四周,这似乎是正确的方法。
我尝试将 total 用作 double 类型,但得到 4.0。
我做错了什么?
【问题讨论】:
你正在做157/32 将两个整数相除,这总是导致向下取整的整数。因此(int) Math.ceil(...) 没有做任何事情。有三种可能的解决方案可以实现您想要的。我建议使用选项 1 或 选项 2。请不使用选项0。
将a 和b 转换为双精度,您可以使用除法和Math.ceil,因为您希望它可以工作。但是我强烈反对使用这种方法,因为双除法可能不精确。要了解有关双精度不精确的更多信息,请参阅this question。
int n = (int) Math.ceil((double) a / b));
int n = a / b + ((a % b == 0) ? 0 : 1);
如果a 和b 都是整数,那么你总是使用 floor 来处理a / b。然后你有一个内联 if 语句女巫检查你是否应该 ceil 而不是 floor。所以+1或+0,如果除法有余数,你需要+1。 a % b == 0 检查余数。
这个选项很短,但可能不太直观。我认为这种不太直观的方法会比双除法和比较方法更快:
请注意,这不适用于b < 0。
int n = (a + b - 1) / b;
为了减少溢出的机会,您可以使用以下方法。但是请注意,它不适用于a = 0 和b < 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 上询问。
【讨论】:
157/32 是 int/int,结果为 int。
尝试使用双字面值 - 157/32d,即int/double,结果为double。
【讨论】:
157/32 是一个整数除法,因为所有数字文字都是整数,除非另外指定后缀(d 表示双精度,l 表示长)
除法被四舍五入(到 4)之前,它被转换为双精度(4.0),然后向上舍入(到 4.0)
如果你使用变量,你可以避免这种情况
double a1=157;
double a2=32;
int total = (int) Math.ceil(a1/a2);
【讨论】:
int total = (int) Math.ceil((double)157/32);
【讨论】:
没有人提到最直观的:
int x = (int) Math.round(Math.ceil((double) 157 / 32));
此解决方案修复了 double 除法不精确。
【讨论】:
在 Java 中添加 .0 将使其成为双倍...
int total = (int) Math.ceil(157.0 / 32.0);
【讨论】:
两个整数相除时,例如,
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/b 的ceil() 实现为-( (-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 将执行此操作。对于整数,它不会将它们推高到下一个整数。对于其他一切,它都会。
哎呀。希望这是有道理的。我确信有一种更优雅的数学方式来解释它。
【讨论】:
还可以将数字从整数转换为实数,您可以添加一个点:
int total = (int) Math.ceil(157/32.);
并且 (157/32.) 的结果也将是真实的。 ;)
【讨论】:
int total = (int) Math.ceil( (double)157/ (double) 32);
【讨论】:
检查以下解决方案以了解您的问题:
int total = (int) Math.ceil(157/32);
这里你应该将分子乘以 1.0,然后它就会给出你的答案。
int total = (int) Math.ceil(157*1.0/32);
【讨论】:
使用 double 来投射像
Math.ceil((double)value) 或喜欢
Math.ceil((double)value1/(double)value2);
【讨论】:
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。由于边缘情况是这方面的棘手之处,我认为需要进一步说明。
有两种方法可以将双精度值四舍五入。
如果您希望您的答案 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);
}
}
【讨论】:
我知道这是一个老问题,但在我看来,我们有一个更好的方法是使用 BigDecimal 来避免精度损失。顺便说一句,使用此解决方案,我们可以使用多种舍入和缩放策略。
final var dividend = BigDecimal.valueOf(157);
final var divisor = BigDecimal.valueOf(32);
final var result = dividend.divide(divisor, RoundingMode.CEILING).intValue();
【讨论】:
int total = (157-1)/32 + 1
或更笼统的
(a-1)/b +1
【讨论】: