/** * Filename: FixedPointLong.java (in org.repin.base.util) * This file is part of the Redpin project. * * Redpin is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * any later version. * * Redpin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Redpin. If not, see <http://www.gnu.org/licenses/>. * * (c) Copyright ETH Zurich, Pascal Brogle, Philipp Bolliger, 2010, ALL RIGHTS RESERVED. * * www.redpin.org */ package org.redpin.base.util; import org.redpin.base.util.FixedPointLong; import org.redpin.base.util.FixedPointLongException; /** * Implements fixed-point numbers in the long primitive type * * TAKEN FROM placelab! * * @version 0.1 */ public final class FixedPointLong { private static final int OFFSET = 32; private static final int LENGTH = 63; /** * make a flong from an int, but without checking for overflow -- use this * only on constants */ public static final long intToFlongSafe(int i) { return ((long) i) << OFFSET; } /** * make a flong from an integer * * @throws FixedPointLongException * if an overflow occurs */ public static final long intToFlong(int i) throws FixedPointLongException { long ans = ((long) i) << OFFSET; if (ans >> OFFSET != i) throw new FixedPointLongException("intToFlong: number too large: " + i); return ans; } /** * make a flong from a string. E-notation numbers (e.g. -783.23E-6) are * understood. * * @throws FixedPointLongException * if the string is not parseable, or if overflow occurs */ public static final long stringToFlong(String s) throws FixedPointLongException { try { boolean negative = false; final int maxAfterPointLen = ("" + (1L << (OFFSET))).length(); /* * ("" + (1L << (LENGTH - 4))).length(); */ if (s == null || s.equals("")) return 0; // split into integer and fractional parts int pointIndex = s.indexOf('.'); int eIndex = s.indexOf('e'); if (eIndex == -1) eIndex = s.indexOf('E'); if (eIndex > 0 && pointIndex > 0 && eIndex <= pointIndex) { throw new FixedPointLongException( "stringToFlong: incorrect number format: \"" + s + "\""); } String beforePoint, afterPoint, eString; if (pointIndex < 0) { afterPoint = "0"; if (eIndex < 0) { beforePoint = s; eString = "0"; } else { beforePoint = s.substring(0, eIndex); eString = s.substring(eIndex + 1, s.length()); } } else { beforePoint = s.substring(0, pointIndex); if (eIndex < 0) { afterPoint = s.substring(pointIndex + 1, s.length()); eString = "0"; } else { afterPoint = s.substring(pointIndex + 1, eIndex); eString = s.substring(eIndex + 1, s.length()); } } // parse and check integer part if (beforePoint.charAt(0) == '-') negative = true; long intpart; try { intpart = Long.parseLong(beforePoint); } catch (NumberFormatException nfe) { throw new FixedPointLongException( "stringToFlong: cannot parse intpart of \"" + s + "\""); } if (negative) intpart = -intpart; // make positive for now if (bitsUsed(intpart) > LENGTH - OFFSET) throw new FixedPointLongException( "stringToFlong: number too large: \"" + s + "\" : bitsUsed " + bitsUsed(intpart)); // parse and check fractional part if (afterPoint.charAt(0) == '-') throw new FixedPointLongException( "stringToFlong: bad number format: \"" + s + "\""); if (afterPoint.length() > maxAfterPointLen) { /* commented out to remove warning */ // String ap = afterPoint; afterPoint = afterPoint.substring(0, maxAfterPointLen); } long fracpart; try { fracpart = Long.parseLong(afterPoint); } catch (NumberFormatException nfe) { throw new FixedPointLongException( "stringToFlong: cannot parse fracpart of \"" + s + "\": " + nfe); } if (fracpart != 0) { int oldbitsused = bitsUsed(fracpart); fracpart = fracpart << LENGTH - oldbitsused; for (int i = 0; i < afterPoint.length() - 1; i++) { fracpart /= 10; } fracpart = fracpart / 10 + (fracpart % 10 >= 5 ? 1 : 0); int shiftback = LENGTH - OFFSET - oldbitsused; if (shiftback > 0) { fracpart = fracpart >> shiftback - 1; fracpart = (fracpart + (fracpart & 1)) >> 1; // rounding } else { fracpart = fracpart << -shiftback; } if (bitsUsed(fracpart) > OFFSET + 1) throw new FixedPointLongException( "stringToFlong: error parsing " + s + " bitsused " + bitsUsed(fracpart)); } // output long result = ((intpart << OFFSET) + fracpart); // now do offset int shift; try { shift = Integer.parseInt(eString); } catch (NumberFormatException nfe) { throw new FixedPointLongException( "stringToFlong: cannot parse epart of \"" + s + "\""); } while (shift > 0) { long oldresult = result; result = result * 10; if (result / 10 != oldresult) throw new FixedPointLongException( "stringToFlong: overflow applying E shift when parsing \"" + s + "\""); shift--; } while (shift < 0) { result = result / 10; shift++; } result = negative ? -result : result; // System.out.println("Flong representation of " + s + " is " + // result + ", intpart " + intValue(result) + ", flongToString " + // flongToString(result)); return result; } catch (Throwable t) { throw new FixedPointLongException( "FixedPointLong.stringToFlong: err " + t.getClass().getName() + ":" + t.getMessage()); } } private static final int bitsUsed(long x) { if (x < 0) x = -x; for (int i = LENGTH - 1; i >= 0; i--) { if (((x >> i) & 1L) == 1) return i + 1; } return 0; } /** Makes a string from a flong, to the full precision available */ public static final String flongToString(long l) throws FixedPointLongException { return flongToString(l, -1); } /** * Makes a string from a flong, with precisely the specified number of * decimal places */ public static final String flongToString(long l, int dp) throws FixedPointLongException { try { boolean negative = l < 0; if (negative) l = -l; long intpart = l >> OFFSET; long fracpart = ((l & ((1L << OFFSET) - 1))); StringBuffer flongToStringBuf = new StringBuffer(); if (fracpart != 0L) { int power10 = 0; while (bitsUsed(fracpart) < LENGTH - 3) { fracpart *= 10L; power10++; } fracpart = fracpart >> OFFSET - 1; fracpart = (fracpart >> 1) + (fracpart & 1); // rounding if (dp > -1) { while (power10 > dp + 1) { fracpart = fracpart / 10; power10--; } if (power10 > dp) { fracpart = (fracpart / 10) + ((fracpart % 10 > 5) ? 1 : 0); power10--; } } flongToStringBuf.append(fracpart); while (flongToStringBuf.length() < power10) flongToStringBuf.insert(0, '0'); if (flongToStringBuf.length() > power10) { // This is overkill // char[] cs = new char[afterPointBuf.length()-power10]; // afterPointBuf.getChars(0,afterPointBuf.length()-power10,cs,0); // long carry=Long.parseLong(new String(cs)); // System.out.println("afterPointBuf.length " + // afterPointBuf.length() + " >power10 " + power10 + " : " + // afterPointBuf + " : intpart " + intpart + ", carry " + // carry); // intpart += carry; // afterPointBuf.delete(0,afterPointBuf.length()-power10); intpart += 1; flongToStringBuf.deleteCharAt(0); } if (dp == -1) { int newlength = flongToStringBuf.length(); while (newlength > 0 && flongToStringBuf.charAt(newlength - 1) == '0') newlength--; flongToStringBuf.setLength(newlength); } else { while (flongToStringBuf.length() < dp) { flongToStringBuf.append('0'); } } if (flongToStringBuf.length() > 0) { flongToStringBuf.insert(0, '.'); } } flongToStringBuf.insert(0, intpart); if (negative) flongToStringBuf.insert(0, '-'); // System.out.println("String rep of flong " + l + " (int " + // intValue(l) + ") is " + flongToStringBuf); return flongToStringBuf.toString(); } catch (Exception e) { throw new FixedPointLongException("flongToString Exception: " + e); } } /** returns a flong representing the integer portion of the parameter flong */ public static final long intPart(long flong) { return (flong >> OFFSET) << OFFSET; } /** * returns a flong representing the fractional portion of the parameter * flong */ public static final long fracPart(long flong) { return flong - intPart(flong); } /** returns the integer value, after rounding, of the flong */ public static final int intValue(long flong) { return (int) (flong >> OFFSET); } /* * program used to generate the table below public static void main(String[] * args) { System.out.print("static final long costable[] = new long[]{"); * for(int i=0;i<=89;i++) { * System.out.print(FixedPointLong.stringToFlong(""+Math.cos(Math.toRadians(i))) + * "L,"); } System.out.println("0L};"); } */ static final long costable[] = new long[] { 4294967296L, 4294967296L, 4294967296L, 4286578688L, 4286578688L, 4278190080L, 4269801472L, 4262985728L, 4253024256L, 4244635648L, 4229955584L, 4215799808L, 4202692608L, 4185915392L, 4169138176L, 4152360960L, 4127195136L, 4110417920L, 4085252096L, 4060086272L, 4034920448L, 4009754624L, 3984588800L, 3951034368L, 3925868544L, 3892314112L, 3860332544L, 3825205248L, 3792175104L, 3758096384L, 3720347648L, 3682598912L, 3642228736L, 3601858560L, 3560964096L, 3519021056L, 3472883712L, 3430940672L, 3384803328L, 3338665984L, 3289907200L, 3241672704L, 3191865344L, 3141533696L, 3091202048L, 3036676096L, 2982150144L, 2927624192L, 2873098240L, 2818572288L, 2759852032L, 2701131776L, 2642411520L, 2583691264L, 2524971008L, 2463629312L, 2403336192L, 2340421632L, 2277507072L, 2210398208L, 2147483648L, 2080374784L, 2013265920L, 1946157056L, 1879048192L, 1811939328L, 1746927616L, 1677721600L, 1608515584L, 1543503872L, 1468006400L, 1392508928L, 1325400064L, 1258291200L, 1191182336L, 1107296256L, 1040187392L, 973078528L, 889192448L, 822083584L, 746586112L, 671088640L, 595591168L, 520093696L, 452984832L, 373293056L, 297795584L, 234881024L, 148897792L, 74973184L, 0L }; /** returns the cosine of a flong, as a flong */ public static final long cos(long degreesFlong) { return cos(intValue(degreesFlong)); } private static final long cos(int degrees) { if (degrees < 0) return cos(-degrees); if (degrees > 90) return -cos(degrees - 180); // System.out.println("cos of " + degrees + " is " + // costable[(int)degrees]); return costable[(int) degrees]; } /** * returns a flong representing the division of the parameter flongs * * @throws FixedPointLongException * if a divide-by-zero occurs */ public static final long div(long topFlong, long bottomFlong) throws FixedPointLongException { int shift = OFFSET; if (bottomFlong == 0) throw new FixedPointLongException("Divide by zero"); if (topFlong == 0) return 0; while (shift > 0 && (topFlong & (1L << LENGTH - 1)) == 0) { shift--; topFlong <<= 1; } while (shift > 0 && (bottomFlong & 1L) == 0) { shift--; bottomFlong = (bottomFlong >> 1) + (bottomFlong & 1L); } return (topFlong / bottomFlong) << shift; } /** * Returns a flong representing the multiple of the two parameter flongs * * @throws FixedPointLongException * if overflow occurs */ public static long mult(long flongOne, long flongTwo) throws FixedPointLongException { boolean negative = false; long a = flongOne; long b = flongTwo; if (flongOne < 0) { a = -a; negative = !negative; } if (flongTwo < 0) { b = -b; negative = !negative; } long intmult = (a >> OFFSET) * (b >> OFFSET); if (intmult >> OFFSET != 0) { throw new FixedPointLongException("Numbers too large to multiply: " + flongToString(flongOne) + ", " + flongToString(flongTwo)); } long fracmult1 = (a >> OFFSET) * fracPart(b); long fracmult2 = (b >> OFFSET) * fracPart(a); long fracmult3 = (fracPart(a) >> 1) * (fracPart(b) >> 1); // System.err.println("intmult " + intmult + " fracmult1 " + fracmult1 + // " fracmult2 " + fracmult2 + " fracmult3 " + fracmult3); // System.err.println("fracpart1 " + fracPart(a) + " fracpart2 " + // fracPart(b) + " fracmult " + fracmult3); long ret = intToFlong((int) intmult) + fracmult1 + fracmult2 + (fracmult3 >> (OFFSET - 2)); if (ret < 0) throw new FixedPointLongException( "Numbers are too large to multiply: " + flongToString(flongOne) + ", " + flongToString(flongTwo)); if (negative) ret = -ret; // System.err.println("" + flongToString(flongOne) + " times " + // flongToString(flongTwo) + " is " + flongToString(ret)); return ret; } /** * returns the flong representing the square of the parameter flong * * @throws FixedPointLongException * if overflow occurs */ public static long square(long flong) throws FixedPointLongException { long ret = mult(flong, flong); // if(ret < 0) System.err.println("error! square of " + // flongToString(flong) + " is negative: " + flongToString(ret)); return ret; } /** * returns the flong representing the positive square root of a flong * * @throws FixedPointLongException * if a negative number is passed in */ public static long sqrt(long flong) throws FixedPointLongException { if (flong < 0) throw new FixedPointLongException( "FixedPointLong: sqrt of -ve number!: " + FixedPointLong.flongToString(flong)); if (flong == 0) return 0; long oldans = 0; long ans = 1L << OFFSET; int i = 0; while (oldans != ans) { i++; // System.out.println("sqrtdebug: " + flongToString(oldans) + " " + // flongToString(ans)); // dirty hack to enforce return if (i > 100) return ans; oldans = ans; ans = (oldans + div(flong, oldans)) >> 1; } return ans; } /** * returns a flong representing sqrt(x*x+y*y), where x and y are the * parameter flongs. Use this rather than doing it yourself because this * won't overflow with numbers who's square is above the representable * limit. */ public static long pythagoras(long x, long y) throws FixedPointLongException { int bitShift = (bitsUsed(x) + bitsUsed(y) + 1 - LENGTH) / 2; if (bitShift < 0) bitShift = 0; // System.err.println("bitsUsed(x) = " + bitsUsed(x) + " bitsUsed(y) = " // + bitsUsed(y) + " bitShift is " + bitShift); x >>= bitShift; y >>= bitShift; /* commented out to remove warning */ // long sumsquares = square(x) + square(y); long ret = sqrt(square(x) + square(y)) << bitShift; // System.err.println("pythag of " + flongToString(xtemp) + " (bu " + // bitsUsed(xtemp) + ") and " + flongToString(ytemp) + " (bu " + // bitsUsed(ytemp) + ") is " + flongToString(ret) + " (bitShift " + // bitShift + ")"); return ret; } /** * rounded to the nearest integer */ public static long intValueRounded(long flong) { // 0x80000000 is "flong" for 0.5 return intValue(flong + 0x80000000L); } }