Best way to shift double value by BigInteger value with Java

Is there a way to convert a double value to a BigInteger value and later? At best, without data loss. The problem is that I do not know how many decimal places have double meanings. But I need this conversion for an Algorithm that only works with non-decimal values. After completing the algorithm, I have to return it.

An easy example of what I need: for example, the sum of two double values, but the "sum" function only works with BigInteger.

+4
source share
3 answers

This program is based on the idea of ​​BigDecimal-and-scale, as in the assylias answer , but modified to unconditionally use the maximum possible scale required. This allows you to use the same scale in the stream of numbers, without seeing all the numbers before processing any of them. The cost is that it usually returns unnecessarily large BigInteger values.

The scale factor, "1e1074", is based on the observation that all finite double numbers are integer multiples of Double.MIN_VALUE, which has 1074 decimal places after the decimal point. You cannot double the number of decimal digits after a decimal point.

import java.math.BigDecimal;
import java.math.BigInteger;

public class Test {
  public static void main(String[] args) {
    testit(Double.MIN_VALUE);
    testit(Double.MAX_VALUE);
    testit(0);
    testit(1.0);
    testit(Math.E);
    testit(Math.PI);
  }

  private static void testit(double d) {
    double roundTrip = scaledIntegerToDouble(doubleToScaledInteger(d));
    if (d != roundTrip) {
      System.out.println("ERROR: " + d + " " + roundTrip);
    }
  }

  public static final BigDecimal scale = new BigDecimal("1e1074");

  public static BigInteger doubleToScaledInteger(double d) {
    BigDecimal bd = new BigDecimal(d).multiply(scale);
    return bd.toBigIntegerExact();
  }

  public static double scaledIntegerToDouble(BigInteger bi) {
    BigDecimal bd = new BigDecimal(bi).divide(scale);
    return bd.doubleValue();
  }
}
+4
source

You can do this in 5 steps:

double d1 = 0.1; //your original double
BigDecimal bd1 = new BigDecimal(d1); //convert to BigDecimal
BigInteger bi = bd1.unscaledValue(); //convert to BigInteger
//here do your stuff with the BigInteger
BigDecimal bd2 = new BigDecimal(bi, bd1.scale()); //back to BigDecimal, applying scale
double d2 = bd2.doubleValue(); //convert to double

The full example applies to the method. sum

Conclusion:

0,1 + 0,1 = 0,2
0,1 + 10,1 = 10,2
0.1245 + 17.0 = 17.1245

:

public static void main(String[] args) {
  test(0.1, 0.1);
  test(0.1, 10.1);
  test(0.1245, 17);
}

private static void test(double d1, double d2) {
  System.out.println(d1 + " + " + d2 + " = " + sum(d1, d2));
}

private static double sum(double d1, double d2) {
  BigDecimal bd1 = new BigDecimal(d1);
  BigDecimal bd2 = new BigDecimal(d2);

  int shift = Integer.max(bd1.scale(), bd2.scale());

  BigInteger bi1 = bd1.scaleByPowerOfTen(shift).toBigInteger();
  BigInteger bi2 = bd2.scaleByPowerOfTen(shift).toBigInteger();

  BigInteger sum = sum(bi1, bi2);

  return new BigDecimal(sum, shift).doubleValue();
}

private static BigInteger sum(BigInteger i1, BigInteger i2) {
  return i1.add(i2);
}
+5
package test;

import java.math.*;

public class HelloWorld{

    public static BigInteger sumBigInteger(BigInteger n1,BigInteger n2){
        return n1.add(n2);
    }

    public static double sumDouble(double n1,double n2){
        int scale=1;
        int max = Math.max(((""+n1).split("\\."))[1].length(), ((""+n2).split("\\."))[1].length());
        for (int i=0;i<max;i++) scale*=10;
        BigInteger nbr1 = new BigDecimal(n1*scale).toBigInteger();
        BigInteger nbr2 = new BigDecimal(n2*scale).toBigInteger();
        return (sumBigInteger(nbr1,nbr2).doubleValue() / scale);
    }

     public static void main(String []args){    
         double n1=117.22 , n2=56.945;
         System.out.println(n1+" + "+n2+" = "+sumDouble(n1,n2));    
     }
}

:

117.22 + 56.945 = 174.165
+1
source

All Articles