• 关于Float.parseFloat()的一点探讨


      最近在解决线上的bug时,遇到一个问题。

    第三方传过来的课程编码时4214410000,然而我们存进数据库的值却变成了4214409980。查遍了所有的代码都查不到有对这个值修改的代码。最后,通过打印日志的方法,找到了这个值改变的代码段。最终确定是Float.parseFloat(“4214410000”)改变了这个值。

    老大告诉我这还不算解决问题,要查查为什么有的课程代码改变了,有的却没有改变,例如4211030020

    然后,我就研究了jdk中Float.parseFloat()的源码。

    在jdk 1.8中 Float.parseFloat()的实现是在FloatingDecimal.cass文件中,其中主要有两个方法,一个是readJavaFormatString()方法,一个是ASCIIToBinaryBuffer()。

    第一个方法有以下几个功能

     1.检验是否是空字符串

    2.字符串是否是以'N' ,'I'开头,或者是否是八进制

    3.给下一个方法提供四个参数,是否是负数,总长度,字符串数组,(总长度-字符串最后连续0的个数)

    ASCIIToBinaryBuffer()获得以上四个参数后就开始生成float。

    在看源码的时候,我发现4214410000生成的float步骤和4211030020不一致,导致不一致的原因是4214410000后面是连续的5个0。所以4214410000生成的float是421441.0* 10000.0得到的结果就是4.21440998E9。结果不是我们预想的整数,这是因为float的特性所决定的。《Effective Java》中已经讲出了这种问题,float/double不能停供完全精确的计算结果。这个原理其实很简单,float/int都是32bit(也就是一共有232个精确值),而int的范围是-231 ~ 231-1,而Float的最大值是3.4028235e+38,远大于231 - 1。而且,int只负责个数有限的整数,而浮点却要用来表示个数无穷的小数,显然力不从心。浮点精确值可以简单视作一个以0为中心的正态分布,绝对值越小(越接近0的地方),相邻两个精确值月密集。比如,最近的两个值可能只相差0.00000...几十个0...01,而最远的两个精确值,却差了2.028241E31。

    到此,问题解决。以下贴上我的测试Float.parseFloat()的代码。

    Float.parseFloat()的实现代码是我从jdk源码拷过来的,使用的是断点调试的方法。

    public class Test {

    static float readJavaFormatString(String var0) throws NumberFormatException {
    boolean var1 = false;
    boolean var2 = false;

    try {
    var0 = var0.trim();
    int var5 = var0.length();
    if (var5 == 0) {
    throw new NumberFormatException("empty String");
    }

    int var6 = 0;
    switch(var0.charAt(var6)) {
    case '-':
    var1 = true;
    case '+':
    ++var6;
    var2 = true;
    }

    char var4 = var0.charAt(var6);

    char[] var21 = new char[var5];
    int var8 = 0;
    boolean var9 = false;
    int var10 = 0;
    int var11 = 0;

    int var12;
    for(var12 = 0; var6 < var5; ++var6) {
    var4 = var0.charAt(var6);
    if (var4 == '0') {
    ++var11;
    } else {
    if (var4 != '.') {
    break;
    }

    if (var9) {
    throw new NumberFormatException("multiple points");
    }

    var10 = var6;
    if (var2) {
    var10 = var6 - 1;
    }

    var9 = true;
    }
    }

    for(; var6 < var5; ++var6) {
    var4 = var0.charAt(var6);
    if (var4 >= '1' && var4 <= '9') {
    var21[var8++] = var4;
    var12 = 0;
    } else if (var4 == '0') {
    var21[var8++] = var4;
    ++var12;
    } else {
    if (var4 != '.') {
    break;
    }

    if (var9) {
    throw new NumberFormatException("multiple points");
    }

    var10 = var6;
    if (var2) {
    var10 = var6 - 1;
    }

    var9 = true;
    }
    }

    var8 -= var12;
    boolean var13 = var8 == 0;
    if (!var13 || var11 != 0) {
    int var3;
    if (var9) {
    var3 = var10 - var11;
    } else {
    var3 = var8 + var12;
    }

    if (var6 < var5 && ((var4 = var0.charAt(var6)) == 'e' || var4 == 'E')) {
    byte var14 = 1;
    int var15 = 0;
    int var16 = 214748364;
    boolean var17 = false;
    ++var6;
    int var18;
    switch(var0.charAt(var6)) {
    case '-':
    var14 = -1;
    case '+':
    ++var6;
    default:
    var18 = var6;
    }

    while(var6 < var5) {
    if (var15 >= var16) {
    var17 = true;
    }

    var4 = var0.charAt(var6++);
    if (var4 < '0' || var4 > '9') {
    --var6;
    break;
    }

    var15 = var15 * 10 + (var4 - 48);
    }

    int var19 = 324 + var8 + var12;
    if (!var17 && var15 <= var19) {
    var3 += var14 * var15;
    } else {
    var3 = var14 * var19;
    }

    if (var6 == var18) {
    throw new NumberFormatException("For input string: "" + var0 + """);
    }
    }

    if (var6 >= var5 || var6 == var5 - 1 && (var0.charAt(var6) == 'f' || var0.charAt(var6) == 'F' || var0.charAt(var6) == 'd' || var0.charAt(var6) == 'D')) {
    if (var13) {
    return var1 ? -0.0f : 0.0f;
    }

    return floatValue(var1,var3,var21,var8);
    }
    }
    }catch (Exception e){
    e.printStackTrace();
    }
    throw new NumberFormatException("For input string: "" + var0 + """);
    }

    private static final int SINGLE_MAX_SMALL_TEN;
    private static final double[] SMALL_10_POW = new double[]{1.0D, 10.0D, 100.0D, 1000.0D, 10000.0D, 100000.0D, 1000000.0D, 1.0E7D, 1.0E8D, 1.0E9D, 1.0E10D, 1.0E11D, 1.0E12D, 1.0E13D, 1.0E14D, 1.0E15D, 1.0E16D, 1.0E17D, 1.0E18D, 1.0E19D, 1.0E20D, 1.0E21D, 1.0E22D};
    private static final float[] SINGLE_SMALL_10_POW = new float[]{1.0F, 10.0F, 100.0F, 1000.0F, 10000.0F, 100000.0F, 1000000.0F, 1.0E7F, 1.0E8F, 1.0E9F, 1.0E10F};
    private static final int MAX_SMALL_TEN;
    private static final double[] BIG_10_POW = new double[]{1.0E16D, 1.0E32D, 1.0E64D, 1.0E128D, 1.0E256D};
    private static final double[] TINY_10_POW = new double[]{1.0E-16D, 1.0E-32D, 1.0E-64D, 1.0E-128D, 1.0E-256D};
    static {
    MAX_SMALL_TEN = SMALL_10_POW.length - 1;
    SINGLE_MAX_SMALL_TEN = SINGLE_SMALL_10_POW.length - 1;
    }
    static float floatValue(Boolean isNegative,int decExponent,char[] digits,int nDigits ) {
    int var1 = Math.min(nDigits, 8);
    int var2 = digits[0] - 48;

    for(int var3 = 1; var3 < var1; ++var3) {
    var2 = var2 * 10 + digits[var3] - 48;
    }

    float var27 = (float)var2;
    int var4 = decExponent - var1;
    int var7;
    if (nDigits <= 7) {
    if (var4 == 0 || var27 == 0.0F) {
    return isNegative ? -var27 : var27;
    }

    if (var4 >= 0) {
    if (var4 <= SINGLE_MAX_SMALL_TEN) {
    var27 *= SINGLE_SMALL_10_POW[var4];
    return isNegative ? -var27 : var27;
    }

    int var5 = 7 - var1;
    if (var4 <= SINGLE_MAX_SMALL_TEN + var5) {
    var27 *= SINGLE_SMALL_10_POW[var5];
    var27 *= SINGLE_SMALL_10_POW[var4 - var5];
    return isNegative ? -var27 : var27;
    }
    } else if (var4 >= -SINGLE_MAX_SMALL_TEN) {
    var27 /= SINGLE_SMALL_10_POW[-var4];
    return isNegative ? -var27 : var27;
    }
    } else if (decExponent >= nDigits && nDigits + decExponent <= 15) {
    long var29 = (long)var2;

    for(var7 = var1; var7 < nDigits; ++var7) {
    var29 = var29 * 10L + (long)(digits[var7] - 48);
    }

    double var31 = (double)var29;
    var4 = decExponent - nDigits;
    var31 *= SMALL_10_POW[var4];
    var27 = (float)var31;
    return isNegative ? -var27 : var27;
    }

    double var28 = (double)var27;
    if (var4 > 0) {
    if (decExponent > 39) {
    return isNegative ? -1.0F: 1.0F ;
    }

    if ((var4 & 15) != 0) {
    var28 *= SMALL_10_POW[var4 & 15];
    }

    if ((var4 >>= 4) != 0) {
    for(var7 = 0; var4 > 0; var4 >>= 1) {
    if ((var4 & 1) != 0) {
    var28 *= BIG_10_POW[var7];
    }

    ++var7;
    }
    }
    } else if (var4 < 0) {
    var4 = -var4;
    if (decExponent < -46) {
    return isNegative ? -0.0F : 0.0F;
    }

    if ((var4 & 15) != 0) {
    var28 /= SMALL_10_POW[var4 & 15];
    }

    if ((var4 >>= 4) != 0) {
    for(var7 = 0; var4 > 0; var4 >>= 1) {
    if ((var4 & 1) != 0) {
    var28 *= TINY_10_POW[var7];
    }

    ++var7;
    }
    }
    }

    var27 = Math.max(1.4E-45F, Math.min(3.4028235E38F, (float)var28));
    if (nDigits > 200) {
    nDigits = 201;
    digits[200] = '1';
    }

    FDBigInteger var30 = new FDBigInteger((long)var2,digits, var1,nDigits);
    var4 = decExponent - nDigits;
    int var8 = Float.floatToRawIntBits(var27);
    int var9 = Math.max(0, -var4);
    int var10 = Math.max(0, var4);
    var30 = var30.multByPow52(var10, 0);
    var30.makeImmutable();
    FDBigInteger var11 = null;
    int var12 = 0;

    do {
    int var13 = var8 >>> 23;
    int var14 = var8 & 8388607;
    int var15;
    int var16;
    if (var13 > 0) {
    var14 |= 8388608;
    } else {
    assert var14 != 0 : var14;

    var15 = Integer.numberOfLeadingZeros(var14);
    var16 = var15 - 8;
    var14 <<= var16;
    var13 = 1 - var16;
    }

    var13 -= 127;
    var15 = Integer.numberOfTrailingZeros(var14);
    var14 >>>= var15;
    var16 = var13 - 23 + var15;
    int var17 = 24 - var15;
    int var18 = var9;
    int var19 = var10;
    if (var16 >= 0) {
    var18 = var9 + var16;
    } else {
    var19 = var10 - var16;
    }

    int var20 = var18;
    int var21;
    if (var13 <= -127) {
    var21 = var13 + var15 + 127;
    } else {
    var21 = 1 + var15;
    }

    var18 += var21;
    var19 += var21;
    int var22 = Math.min(var18, Math.min(var19, var20));
    var18 -= var22;
    var19 -= var22;
    var20 -= var22;
    FDBigInteger var23 = FDBigInteger.valueOfMulPow52((long)var14, var9, var18);
    if (var11 == null || var12 != var19) {
    var11 = var30.leftShift(var19);
    var12 = var19;
    }

    FDBigInteger var24;
    int var25;
    boolean var26;
    if ((var25 = var23.cmp(var11)) > 0) {
    var26 = true;
    var24 = var23.leftInplaceSub(var11);
    if (var17 == 1 && var16 > -126) {
    --var20;
    if (var20 < 0) {
    var20 = 0;
    var24 = var24.leftShift(1);
    }
    }
    } else {
    if (var25 >= 0) {
    break;
    }

    var26 = false;
    var24 = var11.rightInplaceSub(var23);
    }

    var25 = var24.cmpPow52(var9, var20);
    if (var25 < 0) {
    break;
    }

    if (var25 == 0) {
    if ((var8 & 1) != 0) {
    var8 += var26 ? -1 : 1;
    }
    break;
    }

    var8 += var26 ? -1 : 1;
    } while(var8 != 0 && var8 != 2139095040);

    if (isNegative) {
    var8 |= -2147483648;
    }

    return Float.intBitsToFloat(var8);
    }




    public static void main(String args[]) {
    String ss = "4214410000";
    String cc = "4211030020";
    readJavaFormatString(ss);
    readJavaFormatString(cc);
    }

    }
     
  • 相关阅读:
    20175226 2018-2019-2 《Java程序设计》第二周学习总结
    存储管理-页面置换算法(页面淘汰算法)
    存储管理-存储组织
    进程管理-死锁问题
    操作系统-进程管理
    操作系统:进程管理、存储管理、文件管理、作业管理、设备管理
    第十一章 集合框架
    匿名内部类
    第10章 java常用类
    第8章 反射
  • 原文地址:https://www.cnblogs.com/yelele/p/9308132.html
Copyright © 2020-2023  润新知