在溢出的情况下,变量的值是否总是负数?

4

针对此问题,给定N,K和M,找到最大的整数T,使得N*(K^T) <= M。其中N,K和M的值可以达到10^18。因此,使用long long即可。

我尝试使用T进行迭代来解决这个问题。

int T = 0;
long long Kpow = 1;
while(1)
{
  long long prod = N*Kpow;
  if(prod > M)
    break;
  T++;
  Kpow = Kpow*K;
}

但由于N*Kpow的值可能超出long long的范围,因此需要使用一些大整数处理乘积。但我找到了一些其他的代码可以巧妙地处理这种情况。

long long prod = N*Kpow;
if(prod < 0)
  break;

我一直看到,在溢出的情况下,变量的值变为负数。这总是发生,还是有时也会发生正值在溢出的情况下?


2
如果在表达式的计算过程中,结果在数学上没有定义或者不在其类型所能表示的范围内,行为是未定义的。 - PlasmaHH
4个回答

8

从语言的角度来看,有符号整数溢出的行为是未定义的。这意味着任何事情都可能发生 - 它可以是负数,它可以不变,程序可能会崩溃或者它可以在线订披萨。

在实践中最可能发生的情况取决于您运行的处理器架构 - 因此您需要参考平台规格才能知道。

但我猜测您不能保证溢出为负数。以下是一个人为制造的例子:

signed char c = 127;
c += 255;
std::cout << (int)c << '\n';

这在x86上会发生,涉及打印126。但实际上它可能会执行任何操作。

3
编译器的优化级别也可以改变溢出行为,因为编译器可以假设在正确的程序中不会发生溢出,并进行相应的优化。 - Nigel Harper

0

不是的。在溢出的情况下,变量的值并不总是为负数。

对于有符号整数,C11dr §3.7.1 3未定义行为“未定义行为的一个例子是整数溢出时的行为。”因此,在跨编译器和平台上没有测试可以在溢出之后确定可靠工作。

在可能发生溢出之前检测它。

int T = 0;
long long Kpow = 1;
long long Kpow_Max = LLONG_MAX/K;
long long prod_Max = LLONG_MAX/N;

while(1)
{
  if (Kpow > prod_Max) Handle_Overflow();
  long long prod = N*Kpow;
  if(prod > M)
    break;
  T++;
  if (Kpow > Kpow_Max) Handle_Overflow();
  Kpow = Kpow*K;
}

-1

这个问题不能转换为 K^T <= (M + N - 1) / N 吗?

就溢出检测而言,通常加法和减法是按照无符号数进行计算的,根据有符号数进行设置溢出位,根据无符号数进行设置进位/借位位。对于乘法,有符号或无符号乘法的低位结果相同(这就是为什么ARM CPU只有一个有符号/无符号乘法用于32位操作数的64位结果)。如果乘积太大而无法放入接收乘积的寄存器中,则会发生溢出,例如32位乘法产生39位乘积,应该放入32位寄存器中。对于除法,如果除数为零或商太大而无法放入接收商的寄存器中,则可能发生溢出,例如64位被除数除以32位除数,得到40位商。对于乘法和除法,操作数是否带符号并不重要,只要结果的大小适合接收结果的寄存器即可。


这是一个问题,不是答案。 - Eric Lippert
这是原帖中所面临问题的可能解决方案。 - rcgldr

-4

就像在任何其他带有任意长度的有符号整数的情况下一样...如果溢出到符号位的特定位为开,那么溢出会使数字变为负数。

这意味着,如果您的算术结果会导致溢出,并且如果您将变量字的长度加倍,则会将当前的符号位关闭,那么您很可能会得出一个错误的正结果。


2
不是真的。有符号整数溢出的行为是未定义的。在某些系统(例如dps)中,溢出会导致最大值。 - bolov
-1 '溢出会使数字变为负数' 这是完全错误的!溢出是未定义行为。它可以做任何事情... - πάντα ῥεῖ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接