当前位置: 首页 > 面试题库 >

Java中的半精度浮点

诸超
2023-03-14
问题内容

是否在任何地方都有Java库可以对IEEE
754半精度
数字执行计算或将其与双精度数字进行转换?

这些方法中的任何一种都是合适的:

  • 将数字保持为半精度格式,并使用整数算术和位扭曲(如MicroFloat的单精度和双精度)进行计算
  • 以单精度或双精度执行所有计算,转换成半精度以进行传输(在这种情况下,我需要经过良好测试的转换函数。)

编辑 :转换需要100%准确- 输入文件 很多NaN,无穷大和次法线。

相关问题,但仅适用于JavaScript:在Javascript中解压缩半精度浮点数


问题答案:

您可以使用Float.intBitsToFloat()Float.floatToIntBits()在原始浮点值之间进行转换。如果您可以采用截断的精度(而不是舍入),那么只需少量的移位就可以实现转换。

我现在付出了更多的努力,结果却没有一开始就那么简单。现在,该版本已经在我能想到的各个方面进行了测试和验证,我非常有信心它可以为所有可能的输入值产生准确的结果。它支持任一方向上的精确舍入和次正规转换。

// ignores the higher 16 bits
public static float toFloat( int hbits )
{
    int mant = hbits & 0x03ff;            // 10 bits mantissa
    int exp =  hbits & 0x7c00;            // 5 bits exponent
    if( exp == 0x7c00 )                   // NaN/Inf
        exp = 0x3fc00;                    // -> NaN/Inf
    else if( exp != 0 )                   // normalized value
    {
        exp += 0x1c000;                   // exp - 15 + 127
        if( mant == 0 && exp > 0x1c400 )  // smooth transition
            return Float.intBitsToFloat( ( hbits & 0x8000 ) << 16
                                            | exp << 13 | 0x3ff );
    }
    else if( mant != 0 )                  // && exp==0 -> subnormal
    {
        exp = 0x1c400;                    // make it normal
        do {
            mant <<= 1;                   // mantissa * 2
            exp -= 0x400;                 // decrease exp by 1
        } while( ( mant & 0x400 ) == 0 ); // while not normal
        mant &= 0x3ff;                    // discard subnormal bit
    }                                     // else +/-0 -> +/-0
    return Float.intBitsToFloat(          // combine all parts
        ( hbits & 0x8000 ) << 16          // sign  << ( 31 - 15 )
        | ( exp | mant ) << 13 );         // value << ( 23 - 10 )
}
// returns all higher 16 bits as 0 for all results
public static int fromFloat( float fval )
{
    int fbits = Float.floatToIntBits( fval );
    int sign = fbits >>> 16 & 0x8000;          // sign only
    int val = ( fbits & 0x7fffffff ) + 0x1000; // rounded value

    if( val >= 0x47800000 )               // might be or become NaN/Inf
    {                                     // avoid Inf due to rounding
        if( ( fbits & 0x7fffffff ) >= 0x47800000 )
        {                                 // is or must become NaN/Inf
            if( val < 0x7f800000 )        // was value but too large
                return sign | 0x7c00;     // make it +/-Inf
            return sign | 0x7c00 |        // remains +/-Inf or NaN
                ( fbits & 0x007fffff ) >>> 13; // keep NaN (and Inf) bits
        }
        return sign | 0x7bff;             // unrounded not quite Inf
    }
    if( val >= 0x38800000 )               // remains normalized value
        return sign | val - 0x38000000 >>> 13; // exp - 127 + 15
    if( val < 0x33000000 )                // too small for subnormal
        return sign;                      // becomes +/-0
    val = ( fbits & 0x7fffffff ) >>> 23;  // tmp exp for subnormal calc
    return sign | ( ( fbits & 0x7fffff | 0x800000 ) // add subnormal bit
         + ( 0x800000 >>> val - 102 )     // round depending on cut off
      >>> 126 - val );   // div by 2^(1-(exp-127+15)) and >> 13 | exp=0
}

本书
相比,我实现了两个小的扩展,因为16位浮点的通用精度相当低,与较大的浮点类型(通常由于精度高而通常不会注意到)相比,这可能使浮点格式的固有异常在视觉上可以感知。

第一个是toFloat()函数中的这两行:

if( mant == 0 && exp > 0x1c400 )  // smooth transition
    return Float.intBitsToFloat( ( hbits & 0x8000 ) << 16 | exp << 13 | 0x3ff );

字体大小的正常范围内的浮点数采用指数,因此采用数值大小的精度。但这并不是一个平稳的采用,它是分步进行的:切换到下一个更高的指数将导致一半的精度。现在,对于尾数的所有值,精度都保持不变,直到下一次跳转到下一个更高的指数为止。上面的扩展代码通过返回此特定的半浮点值在覆盖的32位浮点范围的地理中心的值,使这些过渡更加平滑。每个正常的半浮点值都精确映射到8192个32位浮点值。返回值应该恰好在这些值的中间。但是在半浮点指数的过渡处,较低的4096值的精度是较高的4096值的两倍,因此覆盖的数字空间仅为另一侧的一半。所有这8192个32位浮点值都映射到相同的半浮点值,因此,无论将8192中的哪一个转换为32位,然后将其转换回32位,都将产生相同的半浮点值
选择了中间的 32位值。扩展现在导致在过渡像更平滑的半一步SQRT(2)的一个因素,因为在正确的显示 图象 下面而左 画面
应该以可视化的尖锐步骤由两个因素不用抗混叠。您可以安全地从代码中删除这两行以获得标准行为。

covered number space on either side of the returned value:
       6.0E-8             #######                  ##########
       4.5E-8             |                       #
       3.0E-8     #########               ########

第二个扩展是在fromFloat()函数中:

    {                                     // avoid Inf due to rounding
        if( ( fbits & 0x7fffffff ) >= 0x47800000 )
...
        return sign | 0x7bff;             // unrounded not quite Inf
    }

此扩展通过保存一些32位值形式(提升为Infinity)来稍微扩展半浮点格式的数字范围。受影响的值为那些没有四舍五入而小于Infinity的值,仅由于四舍五入而变为Infinity的值。如果您不需要此扩展名,则可以安全地删除上面显示的行。

我试图尽可能地优化fromFloat()函数中正常值的路径,由于使用了预先计算和未移位的常量,因此使其可读性降低了。我没有在’toFloat()’上投入过多的精力,因为无论如何它都不会超出查找表的性能。因此,如果速度真的很重要,则可以toFloat()仅使用该函数填充0x10000个元素的静态查找表,然后使用该表进行实际转换。对于当前的x64服务器VM,这大约快3倍,对于x86客户端VM,这大约快5倍。

我在此将代码放入公共领域。



 类似资料:
  • 问题内容: 我正在尝试使用包含大量16位浮点数的javascript读取二进制文件。可以肯定的是它是IEEE标准,低位字节序。将两个字节读入一个int非常简单,但是从那里将其扩展为一个完整的浮点数并没有太大的成功。有什么线索吗? 问题答案: 我最终根据Wikipedia页面上的信息实现了自己的解析器。它可能不是最快的,但是我对此不太担心。这里是那些好奇的人:

  • 问题内容: 如果我们运行以下代码: 它打印: 文字1.2345678990922222中的长尾将被忽略,但1.22222222222222222222中的长尾不会被忽略(变量d中的最后一个十进制数字变为3而不是2)。为什么? 问题答案: 打印a 或a 时看到的位数是Java的默认规则(从和转换为十进制)的结果。 Java的浮点数默认格式使用最少的有效十进制数字来将数字与附近的可表示数字区分开。1个

  • 问题内容: 众所周知,即使是十进制格式的小数点后有固定数字的浮点数也无法准确表示。因此,我有以下程序要测试: 输出如下: 我无法从上述结果中回答两个问题,我正在寻求以下方面的帮助: 为什么使用的双重表示形式,并且看起来很精确,而没有。 为什么返回true? 问题答案: 我怀疑在这里不能正常工作。写入0.1时,获取确切值的一种可靠方法是write 。 “为什么0.1f + 0.2f == 0.3f返

  • 众所周知,浮点数,即使是小数点后固定数字的十进制格式,也不能准确表示。所以我有以下程序要测试: 输出如下: 以上结果中有两个问题我无法回答,我正在寻求帮助: 为什么0.1、0.2和0.3的双重表示看起来很精确,而0.1、0.2却不精确。

  • 问题内容: 是否有比浮点精度更好的数据类型? 问题答案: 小数数据类型 与基于硬件的二进制浮点数不同,十进制模块具有用户可更改的精度(默认为28位),可以与给定问题所需的精度一样大。 如果您对性能问题感到困扰,请查看GMPY