代码之家  ›  专栏  ›  技术社区  ›  dogbane

必要时切换到BigInteger

  •  6
  • dogbane  · 技术社区  · 16 年前

    有没有什么原因让Java不能自动做到这一点,即如果int太小,就切换到BigInteger?这样我们就不用担心溢流了。

    7 回复  |  直到 16 年前
        1
  •  6
  •   Kathy Van Stone    16 年前

    我怀疑对整数和实数使用原语值的决定(出于性能原因)使得这个选项不可能。请注意,Python和Ruby都按您的要求执行。

    BigInteger .

        2
  •  4
  •   polygenelubricants    16 年前

    有没有什么原因让Java不能自动做到这一点,即如果int太小,就切换到BigInteger?

    因为这是一种比Java当前更高层次的编程行为。语言甚至不知道 BigInteger 类及其作用(即它不在JLS中)。它只知道 Integer

    说到装箱/拆箱 int 是原始类型; 是引用类型。不能有一个变量同时包含这两种类型的值。

        3
  •  1
  •   gustafc    16 年前

    你可以把这些值读入 BigInteger s、 然后把它们转换成 long 如果它们足够小的话。

    private final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
    private static List<BigInteger> readAndProcess(BufferedReader rd) throws IOException {
        List<BigInteger> result = new ArrayList<BigInteger>();
        for (String line; (line = rd.readLine()) != null; ) {
            BigInteger bignum = new BigInteger(line);
            if (bignum.compareTo(LONG_MAX) > 0) // doesn't fit in a long
                result.add(bignumCalculation(bignum));
            else result.add(BigInteger.valueOf(primitiveCalculation(bignum.longValue())));
        }
        return result;
    }
    private BigInteger bignumCalculation(BigInteger value) { 
        // perform the calculation 
    }
    private long primitiveCalculation(long value) {
        // perform the calculation
    }
    

    (可以将返回值设为 List<Number> 大整数 和 Long

    表演 变得更好 如果 文件中的大量数字很小,足以容纳一个 长的 (取决于计算的复杂性)。仍然有溢出的风险,这取决于你所做的工作 primitiveCalculation ,并且您现在已经重复了代码,(至少)将潜在的bug增加了一倍,因此您必须确定性能提升是否真的值得。

    但是,如果您的代码与我的示例类似,那么并行化代码可能会获得更多好处,这样计算和I/O就不会在同一个线程上执行—您必须执行一些操作 相当重 对于这样一个体系结构的计算是CPU受限的。

        4
  •  1
  •   tucuxi    16 年前

    public static class MyLong {
        private long l;
        public MyLong(long l) { this.l = l; }
        public void add(MyLong l2) { l += l2.l; }
    }
    
    public static void main(String[] args) throws Exception {
        // generate lots of random numbers
        long ls[] = new long[100000];
        BigDecimal bds[] = new BigDecimal[100000];
        MyLong mls[] = new MyLong[100000];
        Random r = new Random();
        for (int i=0; i<ls.length; i++) {
            long n = r.nextLong();
            ls[i] = n;
            bds[i] = new BigDecimal(n);
            mls[i] = new MyLong(n);
        }
        // time with longs & Bigints
        long t0 = System.currentTimeMillis();
        for (int j=0; j<1000; j++) for (int i=0; i<ls.length-1; i++) {
            ls[i] += ls[i+1];
        }
        long t1 = Math.max(t0 + 1, System.currentTimeMillis());
        for (int j=0; j<1000; j++) for (int i=0; i<ls.length-1; i++) {
            bds[i].add(bds[i+1]);
        }
        long t2 = System.currentTimeMillis();
        for (int j=0; j<1000; j++) for (int i=0; i<ls.length-1; i++) {
            mls[i].add(mls[i+1]);
        }
        long t3 = System.currentTimeMillis();
        // compare times
        t3 -= t2;
        t2 -= t1;
        t1 -= t0;
        DecimalFormat df = new DecimalFormat("0.00");
        System.err.println("long: " + t1 + "ms, bigd: " + t2 + "ms, x"
                + df.format(t2*1.0/t1) + " more, mylong: " + t3 + "ms, x"
                + df.format(t3*1.0/t1) + " more");
    }
    

    在我的系统上生成以下输出:

    长:375ms,大:6296ms,x16.79以上,长:516ms,x1.38以上

    MyLong 课堂只是为了观察拳击的效果,与你的习惯做比较 BigOrLong 班级。

        5
  •  1
  •   Bill K    15 年前

    Java很快——真的很快。它只比c慢2-4倍,有时甚至比大多数其他语言慢10倍(python)到100倍(ruby),比c/Java还要快(顺便说一句,Fortran也很快)

    部分原因是因为它不会为您切换号码类型。它可以,但目前它可以在几个字节内内联一个像“a*5”这样的操作,想象一下如果a是一个对象,它将要经历的困难。它至少是对a的multiply方法的动态调用,这将比a只是一个整数值时慢几百/千倍。

    还可以想象一下,从int转换为BigInteger而不是long会让调试视频游戏变得多么困难(是的,每次我们移动到屏幕的右侧,游戏速度都会降低50倍,代码都是一样的!这怎么可能?!??)

        6
  •  0
  •   Daniel C. Sobral    16 年前

    有可能吗?对。但是它有很多问题。

    参考文献 字面量 . 区别可以用C来表示:

    int i;
    BigInt* bi;
    

    BigInt** bi

    当然,这是通常用于符号的位,硬件指令很大程度上依赖于它。更糟糕的是,如果我们这样做,那么硬件将无法检测溢出并设置标志来指示它。因此,每个操作都必须伴随一些测试,以查看是否发生了和溢出(取决于何时可以检测到溢出)。

    所有这些都会给基本的整数运算增加很多开销,这实际上会抵消您必须从中获得的任何好处。换句话说,假设BigInt比尝试使用int和检测溢出条件(同时处理引用/文字问题)更快。

    更多 表示整数的空间。因此,我们不是将32位存储在堆栈、对象或任何我们使用它们的地方,而是存储64位,例如,使用额外的32位来控制我们是想要引用还是文本。这可能管用,但有一个明显的问题——空间使用不过,我们可能会在64位硬件上看到更多。

    让我们考虑一下如何在C语言中这样做。现在,我没有C#的编程经验,所以我不能编写代码来完成它,但我希望我能给出一个概述。

    public struct MixedInt
    {
       private int i;
       private System.Numeric.BigInteger bi;
    
       public MixedInt(string s) 
       {
          bi = BigInteger.Parse(s);
          if (parsed <= int.MaxValue && parsed => int.MinValue)
          {
              i = (int32) parsed;
              bi = 0;
          }   
       }
    
       // Define all required operations
    }
    

    然而,请注意,这种优化很可能已经是C#的BigInteger的一部分,因为它是作为结构实现的。

        7
  •  -2
  •   ewernli    16 年前

    Java为什么不能 如果int太小,是BigInteger吗?

    这是 静态类型

    在动态类型语言中 Integer 这些加在一起就会产生一个溢出,系统可以自由返回,比如说,一个 Long

    编辑

    鉴于我的回答和评论不清楚,这里我试图提供更多细节,说明为什么我认为静态类型是 主要的

    1) 我们所说的原始类型 静态打字问题;我们不关心动态类型语言。

    2) 对于基元类型,溢出的结果不能转换为 int 因为它不是正确的w.r.t静态输入

       int i = Integer.MAX_VALUE + 1; // -2147483648
    

    3) 对于引用类型,它是相同的,只是我们有自动装箱。尽管如此,这个加法还是不能返回,比如说,a BigInteger 因为它与静态类型系统(A)不匹配 不能铸造到 ).

      Integer j = new Integer( Integer.MAX_VALUE ) + 1; // -2147483648
    

    4) 我们能做的就是把, Number 并在类型 UnboundedNumeric 在内部优化表示(表示独立性)。

     UnboundedNum k = new UnboundedNum( Integer.MAX_VALUE ).add( 1 ); // 2147483648
    

    不过,这并不是最初问题的答案。

    5) 使用动态输入,比如

    var d = new Integer( Integer.MAX_VALUE ) + 1; // 2147483648
    

    将返回一个 长