NumberUtils.java revision 6d607ebe1d7bccd4fdf220f0275207cb452501bd
1/*
2 * Copyright 2013, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32package org.jf.util;
33
34import java.text.DecimalFormat;
35
36public class NumberUtils {
37    private static final int canonicalFloatNaN = Float.floatToRawIntBits(Float.NaN);
38    private static final int maxFloat = Float.floatToRawIntBits(Float.MAX_VALUE);
39    private static final int piFloat = Float.floatToRawIntBits((float)Math.PI);
40    private static final int eFloat = Float.floatToRawIntBits((float)Math.E);
41
42    private static final long canonicalDoubleNaN = Double.doubleToRawLongBits(Double.NaN);
43    private static final long maxDouble = Double.doubleToLongBits(Double.MAX_VALUE);
44    private static final long piDouble = Double.doubleToLongBits(Math.PI);
45    private static final long eDouble = Double.doubleToLongBits(Math.E);
46
47    private static final DecimalFormat format = new DecimalFormat("0.####################E0");
48
49    public static boolean isLikelyFloat(int value) {
50        // Check for some common named float values
51        // We don't check for Float.MIN_VALUE, which has an integer representation of 1
52        if (value == canonicalFloatNaN ||
53                value == maxFloat ||
54                value == piFloat ||
55                value == eFloat) {
56            return true;
57        }
58
59        // Check for some named integer values
60        if (value == Integer.MAX_VALUE || value == Integer.MIN_VALUE) {
61            return false;
62        }
63
64
65        // Check for likely resource id
66        int packageId = value >> 24;
67        int resourceType = value >> 16 & 0xff;
68        int resourceId = value & 0xffff;
69        if ((packageId == 0x7f || packageId == 1) && resourceType < 0x1f && resourceId < 0xfff) {
70            return false;
71        }
72
73        // a non-canocical NaN is more likely to be an integer
74        float floatValue = Float.intBitsToFloat(value);
75        if (Float.isNaN(floatValue)) {
76            return false;
77        }
78
79        // Otherwise, whichever has a shorter scientific notation representation is more likely.
80        // Integer wins the tie
81        String asInt = format.format(value);
82        String asFloat = format.format(floatValue);
83
84        // try to strip off any small imprecision near the end of the mantissa
85        int decimalPoint = asFloat.indexOf('.');
86        int exponent = asFloat.indexOf("E");
87        int zeros = asFloat.indexOf("000");
88        if (zeros > decimalPoint && zeros < exponent) {
89            asFloat = asFloat.substring(0, zeros) + asFloat.substring(exponent);
90        } else {
91            int nines = asFloat.indexOf("999");
92            if (nines > decimalPoint && nines < exponent) {
93                asFloat = asFloat.substring(0, nines) + asFloat.substring(exponent);
94            }
95        }
96
97        return asFloat.length() < asInt.length();
98    }
99
100    public static boolean isLikelyDouble(long value) {
101        // Check for some common named double values
102        // We don't check for Double.MIN_VALUE, which has a long representation of 1
103        if (value == canonicalDoubleNaN ||
104                value == maxDouble ||
105                value == piDouble ||
106                value == eDouble) {
107            return true;
108        }
109
110        // Check for some named long values
111        if (value == Long.MAX_VALUE || value == Long.MIN_VALUE) {
112            return false;
113        }
114
115        // a non-canocical NaN is more likely to be an long
116        double doubleValue = Double.longBitsToDouble(value);
117        if (Double.isNaN(doubleValue)) {
118            return false;
119        }
120
121        // Otherwise, whichever has a shorter scientific notation representation is more likely.
122        // Long wins the tie
123        String asLong = format.format(value);
124        String asDouble = format.format(doubleValue);
125
126        // try to strip off any small imprecision near the end of the mantissa
127        int decimalPoint = asDouble.indexOf('.');
128        int exponent = asDouble.indexOf("E");
129        int zeros = asDouble.indexOf("000");
130        if (zeros > decimalPoint && zeros < exponent) {
131            asDouble = asDouble.substring(0, zeros) + asDouble.substring(exponent);
132        } else {
133            int nines = asDouble.indexOf("999");
134            if (nines > decimalPoint && nines < exponent) {
135                asDouble = asDouble.substring(0, nines) + asDouble.substring(exponent);
136            }
137        }
138
139        return asDouble.length() < asLong.length();
140    }
141}
142