如何确定数字在浮点数中是否可以精确表示?

时间:2018-10-08 15:20:31

标签: java floating-point ieee-754

由于浮点数是以2为底的数字系统,因此不可能直接表示0.24F,因为如果没有recurring decimal period,即{{1 }}或1/3

因此,将浮点数1/3=0.3333...打印回十进制表示形式时,其显示为0.(3),但由于四舍五入而发生了变化:

0.24F

0.23可以直接显示:

println(0.24F) => 0.23999999463558197021484375

但是我如何确定数字可以精确表示?

0.25F

也许Java API已经具有某些功能来做到这一点?

UPD 这是一个代码,显示范围为[-4,4]的浮点数及其内部表示形式:

println(0.25F) => 0.25

当尾数为零时,浮点数绝对准确。但是在其他情况下,例如isExactFloat(0.25F) ==> true isExactFloat(0.24F) ==> false public class FloatDestructure { public static void main(String[] args) { BigDecimal dec = BigDecimal.valueOf(-4000L, 3); BigDecimal incr = BigDecimal.valueOf(1L, 3); for (int i = 0; i <= 8000; i++) { double dbl = dec.doubleValue(); floatDestuct(dbl, dec); dec = dec.add(incr); } } static boolean isExactFloat(double d) { return d == (float) d; } static void floatDestuct(double val, BigDecimal dec) { float value = (float) val; int bits = Float.floatToIntBits(value); int sign = bits >>> 31; int exp = (bits >>> 23 & ((1 << 8) - 1)) - ((1 << 7) - 1); int mantissa = bits & ((1 << 23) - 1); float backToFloat = Float.intBitsToFloat((sign << 31) | (exp + ((1 << 7) - 1)) << 23 | mantissa); boolean exactFloat = isExactFloat(val); boolean exactFloatStr = Double.toString(value).length() <= 7; System.out.println(dec.toString() + " " + (double) val + " " + (double) value + " sign: " + sign + " exp: " + exp + " mantissa: " + mantissa + " " + Integer.toBinaryString(mantissa) + " " + (double) backToFloat + " " + exactFloat + " " + exactFloatStr); } } 并不清楚。

6 个答案:

答案 0 :(得分:3)

通常,这是不可能的。 一旦将数字转换为浮点数或双精度数,它就只是该数字的近似值。因此您对isexactfloat()的输入将不准确...

例如,如果您具有确切的浮点数版本,字符串格式,那么有可能设计一个函数来告诉您float或double是否精确表示字符串格式的数字。请参阅下面的Carlos Heurberger关于如何实现此功能的评论。

答案 1 :(得分:1)

从中创建一个java.lang.ArithmeticException,并捕获{{1}},如果有一个非终止的十进制扩展,它将抛出该错误。

答案 2 :(得分:1)

Java double只能表示终止二进制分数。转换为double可能会隐藏问题,因此我认为最好使用String表示形式。如果字符串表示一个数字,则到BigDecimal的转换是正确的。从floatdoubleBigDecimal的转换也是如此。以下是用于精确表示为floatdouble的测试函数:

  public static boolean isExactDouble(String data) {
    BigDecimal rawBD = new BigDecimal(data);
    double d = rawBD.doubleValue();
    BigDecimal cookedBD = new BigDecimal(d);
    return cookedBD.compareTo(rawBD) == 0;
  }

  public static boolean isExactFloat(String data) {
    BigDecimal rawBD = new BigDecimal(data);
    float d = rawBD.floatValue();
    BigDecimal cookedBD = new BigDecimal(d);
    return cookedBD.compareTo(rawBD) == 0;
  }

答案 3 :(得分:1)

我想在这里分享这个功能。

// Determine whether number is exactly representable in double.
// i.e., No rounding to an approximation during the conversion.
// Results are valid for numbers in the range [2^-24, 2^52].

public static boolean isExactFloat(double val) {

    int exp2 = Math.getExponent(val);
    int exp10 = (int) Math.floor(Math.log10(Math.abs(val)));

    // check for any mismatch between the exact decimal and
    // the round-trip representation.
    int rightmost_bits = (52 - exp2) - (16 - exp10);

    // create bitmask for rightmost bits
    long mask = (1L << rightmost_bits) - 1;

    // test if all rightmost bits are 0's (i.e., no rounding)
    return (Double.doubleToLongBits(val) & mask) == 0;
}

编辑:上面的函数可以更短

public static boolean isExactFloat(double val) {

    int exp2 = Math.getExponent(val);
    int exp10 = (int) Math.floor(Math.log10(Math.abs(val)));

    long bits = Double.doubleToLongBits(val);

    // test if at least n rightmost bits are 0's (i.e., no rounding)
    return Long.numberOfTrailingZeros(bits) >= 36 - exp2 + exp10;        
}

Demo

答案 4 :(得分:0)

您可以只比较doublefloat吗?

public static boolean isExactFloat(double d, float f) {
    return d == f;
}

Demo

答案 5 :(得分:-1)

目前尚不清楚您的问题是与精度(准确地表示0.24)还是重复的数字(例如1 / 3.0)有关。

通常,如果使用常规的浮点表示法,则总是会出现精度问题。

如果精度对您来说确实是一个问题,则应考虑使用BigDecimal。尽管不如double灵活,但它还有其他优点,例如任意精度,您还可以控制非精确计算中的舍入行为(例如重复的十进制值)。

如果您只需要精确控制,那么您可能想看看Apache Commons Math Precision类。