【问题标题】:How does long value fit in float data type in Java without loss of precision长值如何适合 Java 中的浮点数据类型而不损失精度
【发布时间】:2020-01-09 23:52:55
【问题描述】:

long 值如何在不损失精度的情况下适合 Java 中的浮点数据类型。因为如果有精度损失,那么下面的 sn-p 应该会产生

long lMax = Long.MAX_VALUE;
float f = lMax;
System.out.println(lMax == f);

Output 
true

【问题讨论】:

    标签: java floating-point precision long-integer


    【解决方案1】:

    问题是,为了比较long 和float,long 值被强制转换为float,并且发生了与赋值相同的精度损失...

    如果您想“检测”精度损失,请尝试使用BigDecimals,如下所示:

    System.out.println(new BigDecimal(lMax).compareTo(new Bigdecimal(f)));
    

    我知道构造函数BigDecimal(double) 会将float 转换为double,但不会丢失精度...

    编辑

    对于大多数人来说,long 似乎比float 更“精确”。但是float 的范围是+/-10^38,比long 的范围要大很多...

    测试

    long lMax = Long.MAX_VALUE;
    float f = lMax;
    System.out.println( lMax == f );
    System.out.println( new BigDecimal( lMax ).compareTo( new BigDecimal( f ) ) );
    System.out.println( ( (Float) f ).longValue() == lMax );
    System.out.println( ( (Long) lMax ).floatValue() == f );
    System.out.printf( "%d, %d%n", ( (Float) f ).longValue(), lMax );
    
    f = --lMax;
    System.out.println( lMax == f );
    System.out.println( new BigDecimal( lMax ).compareTo( new BigDecimal( f ) ) );
    System.out.println( ( (Float) f ).longValue() == lMax );
    System.out.println( ( (Long) lMax ).floatValue() == f );
    System.out.printf( "%d, %d%n", ( (Float) f ).longValue(), lMax );
    

    输出:

    true
    -1
    true
    true
    9223372036854775807, 9223372036854775807
    true
    -1
    false
    true
    9223372036854775807, 9223372036854775806
    

    【讨论】:

    • 这是另一种方式。浮点值被强制转换为 long。浮动 f2 = 9223372036854775807.5f; System.out.println(lMax == f2);即使这将返回true。请参阅下面的答案。
    【解决方案2】:

    Java 语言规范中的Binary Numeric Promotion 规则是理解问题中代码 sn-p 行为的关键。特别是“否则,如果任一操作数的类型为浮点数,则另一个将转换为浮点数。”

    在lMax == f 比较中,lMax 被转换为浮点数,正如float f = lMax 赋值所做的那样。如果你取相同的输入值,对它进行两次相同的转换,然后比较结果,无论转换是否改变了值,它们都将相等。

    【讨论】:

      【解决方案3】:

      如果您想很好地演示精度损失,请在获取系统时间(以毫秒为单位)并将 long 转换为 float 时尝试这个小脚本:

      class Scratch {
          public static void main(String[] args) throws InterruptedException {
      
              float currentF;
              long currentL;
              float lastNewF = 0;
              long lastL = System.currentTimeMillis();
              long diff;
      
              while (true) {
                  currentL = System.currentTimeMillis();
                  currentF = (float) currentL;
                  if (currentF != lastNewF) {
                      // we finally got a new "different" float value
                      lastNewF = currentF;
                      diff = currentL - lastL;
                      lastL = currentL;
                      System.out.println(diff + " milliseconds since we got a new time as float");
                  }
                  Thread.sleep(250);
              }
          }
      }
      

      您可以从输出中看到,您有时会损失多达 2 分钟的精度:

      0 milliseconds since we got a new time as float
      46964 milliseconds since we got a new time as float
      131054 milliseconds since we got a new time as float
      131093 milliseconds since we got a new time as float
      131077 milliseconds since we got a new time as float
      

      【讨论】:

        【解决方案4】:

        lMax 是一个长数字。当你将它分配给一个浮点数时,它会失去精度。

        float f = lMax;

        当您将 long 与 float 进行比较时,在内部将 long 转换为 float 或 float 转换为 long。你的代码

        lMax == f

        将类似于

        ((Float)f).longValue() == lMax

        这是 9.223372E18 == 9.223372E18 为真

        或

        ((Long)lMax).floatValue() == f

        这是 9223372036854775807 == 9223372036854775807 是真的

        无论哪种方式,答案都是正确的。

        【讨论】:

        • 如果f 丢失精度,将其转换回long 会导致不同的long 值,并且可以看到丢失精度...(一般来说,不是关于Long.MAX_VALUE ...)
        • 不,它会给出相同的结果。检查一次。
        • 我很关心这个问题。总的来说,你说的是对的。
        • Long.MAX_VALUE -1 给出false....(System.out.printf( "%d, %d%n", ( (Float) f ).longValue(), lMax ); 输出:9223372036854775807, 9223372036854775806)
        • 是的,没错。但在这里他正在处理Long.MAX_VALUE。这就是我这么说的原因。
        【解决方案5】:

        lMax的值=9223372036854775807; // 在你的 JVM 中可能会有所不同。

        f 的值 = 9.223372E18

        现在当您比较lMax == f 时,首先将 f 转换为 long,然后进行比较。转换成long后,f值与lMax相同。

        以下代码同样适用。

        long lMax = Long.MAX_VALUE; // value is 9223372036854775807
        float f =lMax;
        float f2 = 9223372036854775807.5f;
        System.out.println(lMax == f);
        System.out.println(lMax == f2);
        

        输出

        true
        true
        

        这里 f2 与 long 相比正在丢失十进制值。 f2的long值与lMax相同。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-02-20
          • 2021-05-09
          • 2018-08-02
          • 2014-02-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多