1 // Copyright (c) 1997, 2004, 2006 Per M.A. Bothner. 2 // This is free software; for terms and warranty disclaimer see ./COPYING. 3 4 package gnu.math; 5 import java.math.BigDecimal; 6 7 public abstract class RealNum extends Complex 8 /* #ifdef JAVA2 */ 9 implements Comparable 10 /* #endif */ 11 { re()12 public final RealNum re() { return this; } im()13 public final RealNum im() { return IntNum.zero(); } 14 angle()15 public final RealNum angle() { 16 return isNegative() ? DFloNum.valueOf(Math.PI) : DFloNum.zero(); 17 } 18 vectorPart()19 @Override public final Quaternion vectorPart() { return IntNum.zero(); } unitVector()20 @Override public final Quaternion unitVector() { return IntNum.zero(); } unitQuaternion()21 @Override public final Quaternion unitQuaternion() { 22 switch (sign()) { 23 case 1: return IntNum.one(); 24 case 0: return IntNum.zero(); 25 case -1: return IntNum.minusOne(); 26 case -2: default: return this; // NaN 27 } 28 } conjugate()29 @Override public final Quaternion conjugate() { return this; } 30 isReal(Object value)31 public static boolean isReal(Object value) { 32 return (value instanceof Number 33 && (value instanceof RealNum || ! (value instanceof Numeric))); 34 } 35 asRealNumOrNull(Object value)36 public static RealNum asRealNumOrNull (Object value) 37 { 38 if (value instanceof RealNum) 39 return (RealNum) value; 40 if (value instanceof Float || value instanceof Double) 41 return new DFloNum(((Number) value).doubleValue()); 42 return RatNum.asRatNumOrNull(value); 43 } 44 isNegative()45 public abstract boolean isNegative (); 46 47 @Override classifyFinite()48 public int classifyFinite() { 49 double d = doubleValue(); 50 return Double.isNaN(d) ? -1 : Double.isInfinite(d) ? 0 : 1; 51 } 52 53 /** Return 1 if {@code >0}; 0 if {@code ==0}; -1 if {@code <0}; -2 if {@code NaN}. */ sign()54 public abstract int sign (); 55 max(RealNum x)56 public RealNum max (RealNum x) 57 { 58 boolean exact = isExact () && x.isExact (); 59 RealNum result = grt (x) ? this : x; 60 if (!exact && result.isExact ()) 61 result = new DFloNum (result.doubleValue ()); 62 return result; 63 } 64 min(RealNum x)65 public RealNum min (RealNum x) 66 { 67 boolean exact = isExact () && x.isExact (); 68 RealNum result = grt (x) ? x : this; 69 if (!exact && result.isExact ()) 70 result = new DFloNum (result.doubleValue ()); 71 return result; 72 } 73 add(RealNum x, RealNum y, int k)74 public static RealNum add (RealNum x, RealNum y, int k) 75 { 76 return (RealNum)(x.add(y, k)); 77 } 78 times(RealNum x, RealNum y)79 public static RealNum times(RealNum x, RealNum y) 80 { 81 return (RealNum)(x.mul(y)); 82 } 83 divide(RealNum x, RealNum y)84 public static RealNum divide (RealNum x, RealNum y) 85 { 86 return (RealNum)(x.div(y)); 87 } 88 89 /* These are defined in Complex, but have to be overridden. */ add(Object obj, int k)90 public abstract Numeric add (Object obj, int k); mul(Object obj)91 public abstract Numeric mul (Object obj); div(Object obj)92 public abstract Numeric div (Object obj); 93 abs()94 public Numeric abs () 95 { 96 return isNegative () ? neg () : this; 97 } 98 rneg()99 public RealNum rneg() { return (RealNum) neg(); } 100 isZero()101 public boolean isZero () 102 { 103 return sign () == 0; 104 } 105 106 /** Convert to an exact number. 107 * Implements the Scheme {@code inexact->exact} (for real numbers). 108 */ toExact()109 public RatNum toExact () 110 { 111 return DFloNum.toExact(doubleValue ()); 112 } 113 toInexact()114 public RealNum toInexact () 115 { 116 if (isExact()) 117 return new DFloNum(doubleValue()); 118 else 119 return this; 120 } 121 122 /** Converts a real to an integer, according to a specified rounding mode. 123 * Note an inexact argument gives an inexact result, following Scheme. 124 * See also RatNum.toExactInt. */ toInt(double d, int rounding_mode)125 public static double toInt (double d, int rounding_mode) 126 { 127 switch (rounding_mode) 128 { 129 case FLOOR: 130 return Math.floor(d); 131 case CEILING: 132 return Math.ceil(d); 133 case TRUNCATE: 134 return d < 0.0 ? Math.ceil (d) : Math.floor (d); 135 case ROUND: 136 return Math.rint(d); 137 default: // Illegal rounding_mode 138 return d; 139 } 140 } 141 142 /** Converts a real to an integer, according to a specified rounding mode. 143 * Note an inexact argument gives an inexact result, following Scheme. 144 * See also toExactInt. */ toInt(int rounding_mode)145 public RealNum toInt (int rounding_mode) 146 { 147 return new DFloNum(toInt(doubleValue(), rounding_mode)); 148 } 149 150 /** Converts to an exact integer, with specified rounding mode. */ toExactInt(int rounding_mode)151 public IntNum toExactInt (int rounding_mode) 152 { 153 return toExactInt(doubleValue(), rounding_mode); 154 } 155 156 /** Converts real to an exact integer, with specified rounding mode. */ toExactInt(double value, int rounding_mode)157 public static IntNum toExactInt (double value, int rounding_mode) 158 { 159 return toExactInt(toInt(value, rounding_mode)); 160 } 161 162 /** Converts an integral double (such as a toInt result) to an IntNum. */ toExactInt(double value)163 public static IntNum toExactInt (double value) 164 { 165 if (Double.isInfinite (value) || Double.isNaN (value)) 166 throw new ArithmeticException ("cannot convert "+value+" to exact integer"); 167 long bits = Double.doubleToLongBits (value); 168 boolean neg = bits < 0; 169 int exp = (int) (bits >> 52) & 0x7FF; 170 bits &= 0xfffffffffffffL; 171 if (exp == 0) 172 bits <<= 1; 173 else 174 bits |= 0x10000000000000L; 175 if (exp <= 1075) 176 { 177 int rshift = 1075 - exp; 178 if (rshift > 53) 179 return IntNum.zero(); 180 bits >>= rshift; 181 return IntNum.make (neg ? -bits : bits); 182 } 183 return IntNum.shift (IntNum.make (neg ? -bits : bits), exp - 1075); 184 } 185 exp()186 public Complex exp () 187 { 188 return new DFloNum(Math.exp(doubleValue())); 189 } 190 log()191 public Complex log () 192 { 193 double x = doubleValue(); 194 if (x <= 0) 195 return DComplex.log(x, 0.0); 196 return new DFloNum(Math.log(x)); 197 } 198 sin()199 @Override public final RealNum sin() { 200 return new DFloNum(Math.sin(doubleValue())); 201 } cos()202 @Override public final RealNum cos() { 203 return new DFloNum(Math.cos(doubleValue())); 204 } tan()205 @Override public final RealNum tan() { 206 return new DFloNum(Math.tan(doubleValue())); 207 } 208 sqrt()209 public final Complex sqrt () 210 { 211 double d = doubleValue(); 212 if (d >= 0) 213 return new DFloNum(Math.sqrt(d)); 214 else 215 return Complex.make(IntNum.zero(), new DFloNum(Math.sqrt(-d))); 216 } 217 218 /** Convert double to (rounded) integer, after multiplying by 10**k. */ toScaledInt(double f, int k)219 public static IntNum toScaledInt (double f, int k) 220 { 221 return toScaledInt(DFloNum.toExact(f), k); 222 } 223 224 /** Convert rational to (rounded) integer, after multiplying by 10**k. */ toScaledInt(RatNum r, int k)225 public static IntNum toScaledInt (RatNum r, int k) 226 { 227 if (k != 0) 228 { 229 IntNum power = IntNum.power(IntNum.ten(), k < 0 ? -k : k); 230 IntNum num = r.numerator(); 231 IntNum den = r.denominator(); 232 if (k >= 0) 233 num = IntNum.times(num, power); 234 else 235 den = IntNum.times(den, power); 236 r = RatNum.make(num, den); 237 } 238 return r.toExactInt(ROUND); 239 } 240 241 /** Convert this to (rounded) integer, after multiplying by 10**k. */ toScaledInt(int k)242 public IntNum toScaledInt (int k) 243 { 244 return toScaledInt(toExact(), k); 245 } 246 247 /* 248 public static String toScaledIntString (double f, int k) 249 { 250 switch (k) 251 { 252 case 0: break; 253 case 1: f = f * 10; break; 254 case 2: f = f * 100; break; 255 case 3: f = f * 1000; break; 256 default: return toScaledInt(f, k).toString(); 257 } 258 return Long.toString((long) f); 259 } 260 */ 261 262 /** Implements the Comparable interface. 263 * This ordering isn't fully consistent with equals, since say 264 * it returns 0 when comparing 1.5 and 3/2, though they are not equals. 265 */ compareTo(Object o)266 public int compareTo(Object o) 267 { 268 return compare(o); 269 } 270 asBigDecimal()271 public java.math.BigDecimal asBigDecimal () 272 { 273 return new BigDecimal(doubleValue()); 274 } 275 toStringScientific(float d)276 public static String toStringScientific (float d) 277 { 278 return toStringScientific(Float.toString(d)); 279 } 280 toStringScientific(double d)281 public static String toStringScientific (double d) 282 { 283 return toStringScientific(Double.toString(d)); 284 } 285 286 /** Convert result of Double.toString or Float.toString to 287 * scientific notation. 288 * Does not validate the input. 289 */ toStringScientific(String dstr)290 public static String toStringScientific (String dstr) 291 { 292 int indexE = dstr.indexOf('E'); 293 if (indexE >= 0) 294 return dstr; 295 int len = dstr.length(); 296 // Check for "Infinity" or "NaN". 297 char ch = dstr.charAt(len-1); 298 if (ch == 'y' || ch == 'N') 299 return dstr; 300 StringBuffer sbuf = new StringBuffer(len+10); 301 int exp = toStringScientific(dstr, sbuf); 302 sbuf.append('E'); 303 sbuf.append(exp); 304 return sbuf.toString(); 305 } 306 toStringScientific(String dstr, StringBuffer sbuf)307 public static int toStringScientific (String dstr, StringBuffer sbuf) 308 { 309 boolean neg = dstr.charAt(0) == '-'; 310 if (neg) 311 sbuf.append('-'); 312 int pos = neg ? 1 : 0; 313 int exp; 314 int len = dstr.length(); 315 if (dstr.charAt(pos) == '0') 316 { // Value is < 1.0. 317 int start = pos; 318 for (;;) 319 { 320 if (pos == len) 321 { 322 sbuf.append("0"); 323 exp = 0; 324 break; 325 } 326 char ch = dstr.charAt(pos++); 327 if (ch >= '0' && ch <= '9' && (ch != '0' || pos == len)) 328 { 329 sbuf.append(ch); 330 sbuf.append('.'); 331 exp = ch == '0' ? 0 : start - pos + 2; 332 if (pos == len) 333 sbuf.append('0'); 334 else 335 { 336 while (pos < len) 337 sbuf.append(dstr.charAt(pos++)); 338 } 339 break; 340 } 341 } 342 } 343 else 344 { 345 // Number of significant digits in string. 346 int ndigits = len - (neg ? 2 : 1); 347 int dot = dstr.indexOf('.'); 348 // Number of fractional digits is len-dot-1. 349 // We want ndigits-1 fractional digits. Hence we need to move the 350 // decimal point ndigits-1-(len-dot-1) == ndigits-len+dot positions 351 // to the left. This becomes the exponent we need. 352 exp = ndigits - len + dot; 353 sbuf.append(dstr.charAt(pos++)); // Copy initial digit before point. 354 sbuf.append('.'); 355 while (pos < len) 356 { 357 char ch = dstr.charAt(pos++); 358 if (ch != '.') 359 sbuf.append(ch); 360 } 361 } 362 // Remove excess zeros. 363 pos = sbuf.length(); 364 int slen = -1; 365 for (;;) 366 { 367 char ch = sbuf.charAt(--pos); 368 if (ch == '0') 369 slen = pos; 370 else 371 { 372 if (ch == '.') 373 slen = pos + 2; 374 break; 375 } 376 } 377 if (slen >= 0) 378 sbuf.setLength(slen); 379 return exp; 380 } 381 toStringDecimal(String dstr)382 public static String toStringDecimal (String dstr) 383 { 384 int indexE = dstr.indexOf('E'); 385 if (indexE < 0) 386 return dstr; 387 int len = dstr.length(); 388 // Check for "Infinity" or "NaN". 389 char ch = dstr.charAt(len-1); 390 if (ch == 'y' || ch == 'N') 391 return dstr; 392 StringBuffer sbuf = new StringBuffer(len+10); 393 boolean neg = dstr.charAt(0) == '-'; 394 if (dstr.charAt(indexE+1) != '-') 395 { 396 throw new UnsupportedOperationException("not implemented: toStringDecimal given non-negative exponent: "+dstr); 397 } 398 else 399 { 400 int pos = indexE+2; // skip "E-". 401 int exp = 0; 402 while (pos < len) 403 exp = 10 * exp + (dstr.charAt(pos++) - '0'); 404 if (neg) 405 sbuf.append('-'); 406 sbuf.append("0."); 407 while (--exp > 0) sbuf.append('0'); 408 for (pos = 0; (ch = dstr.charAt(pos++)) != 'E'; ) 409 { 410 if (ch != '-' & ch != '.' 411 && (ch != '0' || pos < indexE)) 412 sbuf.append(ch); 413 } 414 return sbuf.toString(); 415 } 416 } 417 } 418