diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 29413b881c6..2a6cd622d29 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,9 @@ +2001-11-25 Tom Tromey + + Fix for PR libgcj/4583: + * java/math/BigDecimal.java (BigDecimal(double)): Rewrote. + (BigDecimal(String)): Likewise. + 2001-11-19 Tom Tromey * verify.cc (_Jv_BytecodeVerifier::branch_prepass) [op_iinc]: diff --git a/libjava/java/math/BigDecimal.java b/libjava/java/math/BigDecimal.java index 6fbd9271ee9..bf803a2f526 100644 --- a/libjava/java/math/BigDecimal.java +++ b/libjava/java/math/BigDecimal.java @@ -28,7 +28,8 @@ package java.math; import java.math.BigInteger; -public class BigDecimal extends Number implements Comparable { +public class BigDecimal extends Number implements Comparable +{ private BigInteger intVal; private int scale; private static final long serialVersionUID = 6108874887143696463L; @@ -63,16 +64,119 @@ public class BigDecimal extends Number implements Comparable { public BigDecimal (double num) throws NumberFormatException { - this (Double.toString (num)); + if (Double.isInfinite (num) || Double.isNaN (num)) + throw new NumberFormatException ("invalid argument: " + num); + // Note we can't convert NUM to a String and then use the + // String-based constructor. The BigDecimal documentation makes + // it clear that the two constructors work differently. + + final int mantissaBits = 52; + final int exponentBits = 11; + final long mantMask = (1L << mantissaBits) - 1; + final long expMask = (1L << exponentBits) - 1; + + long bits = Double.doubleToLongBits (num); + long mantissa = bits & mantMask; + long exponent = (bits >>> mantissaBits) & expMask; + boolean denormal = exponent == 0; + // Correct the exponent for the bias. + exponent -= denormal ? 1022 : 1023; + // Now correct the exponent to account for the bits to the right + // of the decimal. + exponent -= mantissaBits; + // Ordinary numbers have an implied leading `1' bit. + if (! denormal) + mantissa |= (1L << mantissaBits); + + // Shave off factors of 10. + while (exponent < 0 && (mantissa & 1) == 0) + { + ++exponent; + mantissa >>= 1; + } + + intVal = BigInteger.valueOf (bits < 0 ? - mantissa : mantissa); + if (exponent < 0) + { + // We have MANTISSA * 2 ^ (EXPONENT). + // Since (1/2)^N == 5^N * 10^-N we can easily convert this + // into a power of 10. + scale = (int) (- exponent); + BigInteger mult = BigInteger.valueOf (5).pow (scale); + intVal = intVal.multiply (mult); + } + else + { + intVal = intVal.shiftLeft ((int) exponent); + scale = 0; + } } public BigDecimal (String num) throws NumberFormatException { - int point = num.indexOf('.'); - this.intVal = new BigInteger (point == -1 ? num : - num.substring (0, point) + - num.substring (point + 1)); - scale = num.length() - (point == -1 ? num.length () : point + 1); + int len = num.length(); + int start = 0, point = 0; + int dot = -1; + boolean negative = false; + if (num.charAt(0) == '+') + { + ++start; + ++point; + } + else if (num.charAt(0) == '-') + { + ++start; + ++point; + negative = true; + } + + while (point < len) + { + char c = num.charAt (point); + if (c == '.') + { + if (dot >= 0) + throw new NumberFormatException ("multiple `.'s in number"); + dot = point; + } + else if (c == 'e' || c == 'E') + break; + else if (Character.digit (c, 10) < 0) + throw new NumberFormatException ("unrecognized character: " + c); + ++point; + } + + String val; + if (dot >= 0) + { + val = num.substring (start, dot) + num.substring (dot + 1, point); + scale = point - 1 - dot; + } + else + { + val = num.substring (start, point); + scale = 0; + } + if (val.length () == 0) + throw new NumberFormatException ("no digits seen"); + + if (negative) + val = "-" + val; + intVal = new BigInteger (val); + + // Now parse exponent. + if (point < len) + { + int exp = Integer.parseInt (num.substring (point + 1)); + exp -= scale; + if (exp > 0) + { + intVal = intVal.multiply (BigInteger.valueOf (10).pow (exp)); + scale = 0; + } + else + scale = - exp; + } } public static BigDecimal valueOf (long val) @@ -338,7 +442,6 @@ public class BigDecimal extends Number implements Comparable { intVal.divide (BigInteger.valueOf (10).pow (scale)); } - public int intValue () { return toBigInteger ().intValue ();