【问题标题】:Java arithmetic int vs. longJava 算术 int 与 long
【发布时间】:2010-06-19 00:38:43
【问题描述】:

我在 google 上进行了一些搜索,但似乎找不到我要找的东西。我试图找出一些关于 Java 中算术工作方式的详细信息。例如,如果将两个 long 加在一起,这是否使用了将两个 int 加在一起时使用的相同加法运算符?此外,当您在算术表达式中混合 long 和 int 时,幕后会发生什么:

long result;
long operand a = 1;
int operand b = 999999999;

result = a + b;

如果有人能对此有所了解或发布相关文章的链接,那就太棒了。谢谢!

编辑:感谢到目前为止的回复。此外,只要您在表达式中分配给最宽基元类型的变量,使用不同基元执行算术是否安全?

【问题讨论】:

  • 这不是一个论坛,对于回答您的问题的人来说,您有后续跟进并不是很透明。至于您的编辑:如果变量不是“宽”到足以容纳它的原始类型,Java 将不允许您将操作的结果分配给变量。换句话说,如果不专门转换为 int,则不能将“0L + 1”的结果分配给 int。
  • 啊好吧,有道理。感谢您的跟进。
  • 它并不完全安全,因为许多操作可能导致下溢或上溢。尝试 Long.MAX_VALUE + 1 但是,如果您分配给最宽的类型,它将始终编译。

标签: java math


【解决方案1】:

当混合类型时,int 会自动加宽为 long,然后将两个 long 相加以产生结果。 Java Language Specification 解释了包含不同原始类型的操作的过程。

具体来说,这些是每个基元无需强制转换即可扩展的类型:

  • byte 到 short、int、long、float 或 double
  • 短于 int、long、float 或 double
  • char 转换为 int、long、float 或 double
  • int 到 long、float 或 double
  • long to float 或 double
  • 浮点数加倍

【讨论】:

【解决方案2】:

见:Conversions and promotions

据此,您的int 被提升为long,然后被评估。

例如,int + double 和其他原语也会发生同样的情况。即。

System.out( 1 + 2.0 );// prints 3.0 a double

至于加法运算符我也差不多,但我没有任何参考。

快速查看编译器的源代码会发现它们是不同的。

iadd 用于int 加法,ladd 用于long 加法:

查看此示例代码:

$cat Addition.java 
public class Addition {
    public static void main( String [] args ) {
        int  a  = Integer.parseInt(args[0]);
        long b = Long.parseLong(args[0]);
        // int addition 
        int  c  = a + a;
        // long addition 
        long d = a + b;
    }
}
$javac Addition.java 
$

编译生成的字节码是这样的:

$javap -c Addition 
Compiled from "Addition.java"
public class Addition extends java.lang.Object{
public Addition();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   aload_0
   1:   iconst_0
   2:   aaload
   3:   invokestatic    #2; //Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
   6:   istore_1
   7:   aload_0
   8:   iconst_0
   9:   aaload
   10:  invokestatic    #3; //Method java/lang/Long.parseLong:(Ljava/lang/String;)J
   13:  lstore_2
   14:  iload_1
   15:  iload_1
   16:  iadd
   17:  istore  4
   19:  iload_1
   20:  i2l
   21:  lload_2
   22:  ladd
   23:  lstore  5
   25:  return

}

16 行它说:iadd(用于int 加法)而22 行说ladd(用于长加法)

此外,只要您在表达式中分配给最广泛的原始类型的变量,使用不同的原始类型执行算术是否安全?

是的,执行较小尺寸的算术也是“安全”,从某种意义上说,它们不会破坏程序,您只会丢失信息。

例如,尝试将Integer.MAX_VALUE 添加到Integer.MAX_VALUE 以查看会发生什么,或者int x = ( int ) ( Long.MAX_VALUE - 1 );

【讨论】:

    【解决方案3】:

    此外,只要您在表达式中分配给最广泛的原始类型的变量,使用不同的原始类型执行算术是否安全?

    这取决于您所说的安全。它当然不会避免你需要考虑溢出的可能性。

    【讨论】:

      【解决方案4】:

      这称为算术提升。详情请看http://www.cafeaulait.org/course/week2/22.html

      【讨论】:

        【解决方案5】:
        int a=5;  
        long b=10;  
        a+=b;  
        System.out.println(a);
        

        主要区别在于a = a + b 不会进行类型转换,因此编译器会因为你不进行类型转换而生气。 但是对于a += b,它真正做的是将b 类型转换为与a 兼容的a

        【讨论】:

        • 所以它正在从longint 进行无形的沮丧,可能会丢弃信息?刚刚在java.io.ByteArrayInputStream#skip(long) 中遇到了这个问题,并做了两遍。以前从未见过。
        【解决方案6】:

        这个简单的例子可以解决java中算术运算中数据类型转换的疑惑。

            byte a = 1;
            short b = 1;
            System.out.println(a+b);                     //Output = 2
            Object o = a+b;                              //Object becomes int
            System.out.println(o.getClass().getName());  //Output = java.lang.Integer
        
            int c = 1;
            System.out.println(a+b+c);                   //Output = 3
            o = a+b+c;                                   //Object becomes int
            System.out.println(o.getClass().getName());  //Output = java.lang.Integer
        
            long d = 1l;
            System.out.println(a+b+c+d);                 //Output = 4
            o = a+b+c+d;                                 //Object becomes long
            System.out.println(o.getClass().getName());  //Output = java.lang.Long
        
            float e = 1.0f;
            System.out.println(a+b+c+d+e);               //Output = 5.0
            o = a+b+c+d+e;                               //Object becomes float
            System.out.println(o.getClass().getName());  //Output = java.lang.Float
        
            double f = 1.0;
            System.out.println(a+b+c+d+e+f);             //Output = 6.0
            o = a+b+c+d+e+f;                             //Object becomes double
            System.out.println(o.getClass().getName());  //Output = java.lang.Double
        

        以下是java中数据类型按范围排列的顺序:

            byte<short<int<long<float<double
        

        这是扩展发生的顺序。

        注意:float 的范围比 long 大,但尺寸更小。

        【讨论】:

          【解决方案7】:

          答案指向算术提升。但考虑到这一点,我陷入了以下陷阱(我感到羞耻),我想在这里指出: 如果对我来说有两个以上的操作数,则似乎从左到右执行计算,并且如果焦点中的实际两个操作数具有不同的类型,则会发生算术提升。 因此,以下代码对 s1 和 s2 产生不同的结果。我对此的解释是: 在计算 s1 时,首先将 a 和 b 作为两个整数相加(并发生溢出),然后将 c(长整数)相加。 在计算 s2 时,先将 c 和 b 添加(只要),然后再添加 a(提升为 long),因此不会发生溢出。

          final int a = Integer.MAX_VALUE;
          final int b = 1;
          final long c = 1L;
          final long s1 = a + b + c;
          final long s2 = c + b + a;
          System.out.println("s1=" + s1 + ", s2=" + s2);
          

          输出将是: s1=-2147483647, s2=2147483649

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-02-01
            • 1970-01-01
            • 2011-05-08
            • 1970-01-01
            • 2020-09-03
            • 2012-12-17
            • 2019-09-22
            • 1970-01-01
            相关资源
            最近更新 更多