Daniel Müssig
Daniel Müssig

Reputation: 1602

Best way for shifting an double value to BigInteger value with Java

Is there a proper way for converting a double value to a BigInteger value and later back? In the best case without loosing data. The problem is, that I don't know how many decimal places the double values have. But I need this conversion for an Algorithm which only works with non decimal values. After the Algorithm finishes I have to convert it back.

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

Upvotes: 4

Views: 2529

Answers (3)

Patricia Shanahan
Patricia Shanahan

Reputation: 26185

This program is based on the BigDecimal-and-scale idea, as in assylias's answer, but modified to unconditionally use the maximum possible required scale. This allows the same scale to be used across a stream of numbers, without seeing all the numbers before processing any of them. The cost is that it will usually return 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 digits after the decimal point. No double can have more decimal digits after the 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();
  }
}

Upvotes: 4

MCHAppy
MCHAppy

Reputation: 1002

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));    
     }
}

Output:

117.22 + 56.945 = 174.165

Upvotes: 1

assylias
assylias

Reputation: 328598

You can do it 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

Full example applied to a sum method

Output:

0.1 + 0.1 = 0.2
0.1 + 10.1 = 10.2
0.1245 + 17.0 = 17.1245

Code:

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);
}

Upvotes: 5

Related Questions