奇怪的结果增加一个超过其最大值的空头

Posted

技术标签:

【中文标题】奇怪的结果增加一个超过其最大值的空头【英文标题】:Weird Result Incrementing a Short Beyond its Maximum Value 【发布时间】:2015-01-02 18:49:54 【问题描述】:

当我执行这段代码时,它给出的 s 值为 -7616。 为什么这样?这是因为在将数据从 int 或其他东西转换为 short 时丢失数据吗?

public static void main(String[] args) 
        // TODO code application logic here
       short s=0;
       int x=123456;
       int i=8;
       s +=x;
       System.out.println(s);

    

【问题讨论】:

【参考方案1】:

你只是溢出了 short 的最大值:

short:short 数据类型是 16 位有符号二进制补码 整数。它的最小值为 -32,768,最大值为 32,767(含)。与字节一样,适用相同的准则:您可以 在大型数组中使用short来节省内存,在这种情况下 节省内存实际上很重要。

出现这种溢出时会发生什么,相当于这个算法:

/** Returns an integer which is equal to the short obtained by the ((short) n) conversion */
public static int int2short(int n) 
    int sign      = n > 0 ? 1 : -1;
    int increment = sign * (Short.MAX_VALUE - Short.MIN_VALUE + 1);
    for ( ; n > Short.MAX_VALUE || n < Short.MIN_VALUE ; n -= increment);
    return n;

【讨论】:

【参考方案2】:

好问题!这让我想到了我很久没有想到的事情,我不得不复习几个概念。谢谢你帮我把脑子里的铁锈敲掉。

对我来说,这种类型的问题最好以二进制形式可视化(原因很快就会变得明显):

你的原始数字(原谅前导零;我喜欢 4 组):

0001 1110 0010 0100 0000

然而,根据 Java 语言规范 (JLS) section 4.2,short 是一个 16 位有符号二进制补码整数。将整数值 123456 分配给一个 short 称为“缩小原语转换”,这在 JLS 5.1.3 中有所介绍。具体来说,“有符号整数到整数类型 T 的窄化转换只会丢弃除 n 个最低位之外的所有位,其中 n 是用于表示类型 T 的位数。”

丢弃除最低 16 位以外的所有位,我们得到:

1110 0010 0100 0000

在无符号整数中,此值为 57,290,但短整数是有符号二进制补码整数。最左边的 1 表示负数;要获取数字的值,您必须invert the bits and add 1:

原文:

1110 0010 0100 0000

反转位:

0001 1101 1011 1111

加1:

0001 1101 1100 0000

将其转换为十进制并加上负号得到-7,616

再次感谢您提出问题。不知道一些事情没关系,所以请继续询问和学习。我回答得很开心...我喜欢潜入 JLS,我知道,很疯狂!

【讨论】:

感谢您的详细解释。我期待这样的答案,因为整数短的不同值给出不同的值,这让我感到困惑!再次感谢!! @Dici,因为它就是这样做的。如果您想要比评论更详细的信息,我帖子中的最后一个链接解释了为什么反转数字并添加一个可以获得二进制补码负值的原因。补码上的Wikipedia entry 对该主题的处理更为严格。【参考方案3】:

short 增加超过其最大值称为溢出。当溢出发生时,该值变为该类型的最小值,并重新开始计数。

以下是尝试将 0+123456 存储在 short 中得到 -7616 的方法:

0 --> 32767

-32768 --> 32767

-32768 --> -7616

换句话说,

32768+ 32768+ 32768+ (32768 -7616) = 123456

【讨论】:

【参考方案4】:

复合赋值运算符+=(真的都是)cast their result

E1 op= E2 形式的复合赋值表达式是等价的 到E1 = (T) ((E1) op (E2)),其中TE1的类型,除了E1 只评估一次。

所以

s += x;

变成

s = (short) (s + x);

现在,因为sshortxint,所以binary numeric promotion is performed on the short value before the addition is applied. short 值被转换为int(这不是问题)。

s = (short) (8 + 123456)
s = (short) (123464)

cast 应用了 narrowing primitive conversion

[...] 可能会丢失有关 数值的整体大小,也可能会丢失精度和 范围。

事情就是这样

s = -7608

【讨论】:

以上是关于奇怪的结果增加一个超过其最大值的空头的主要内容,如果未能解决你的问题,请参考以下文章

狂跌近8%!比特币不行了?最大"空头"来袭!

超过 (65536) 时如何增加 WCF 服务的传入消息的最大消息大小配额

我进行分析时内存的奇怪行为

有没有一种特定的方法可以在一个输入中上传超过30张照片,而不会增加最大上传大小?

Oracle 标识符最大长度

WebService 之 已超过传入消息(65536)的最大消息大小配额。若要增加配额,请使用相应绑定元素上的 MaxReceivedMessageSize 属性。