指向Java
是否有一个Java库可以在IEEE 754半精度数字上执行计算或将其转换为双精度数据或将其转换为双精度数据?
这些方法都适合:
编辑 :转换需要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位并返回相同的半浮点值。 现在,扩展可以在下图右侧显示的过渡处产生类似平滑的半步(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服务器虚拟机的速度要快大约3倍,而使用x86客户端虚拟机的速度要快大约5倍。
我将代码写入公有领域。
x4u的代码将值1正确编码为0x3c00(ref:https://en.wikipedia.org/wiki/Half-precision_floating-point_format)。 但具有平滑性改进的解码器将其解码为1.000122。 维基百科条目说整数值0..2048可以完全表示。 不太好...
从toFloat代码中删除"| 0x3ff"
可以确保toFloat(fromFloat(k)) == k
适用于-2048..2048范围内的整数k,可能会导致平滑度稍差。
在我看到这里发布的解决方案之前,我已经提出了一些简单的方法:
public static float toFloat(int nHalf)
{
int S = (nHalf >>> 15) & 0x1;
int E = (nHalf >>> 10) & 0x1F;
int T = (nHalf ) & 0x3FF;
E = E == 0x1F
? 0xFF // it's 2^w-1; it's all 1's, so keep it all 1's for the 32-bit float
: E - 15 + 127; // adjust the exponent from the 16-bit bias to the 32-bit bias
// sign S is now bit 31
// exp E is from bit 30 to bit 23
// scale T by 13 binary digits (it grew from 10 to 23 bits)
return Float.intBitsToFloat(S << 31 | E << 23 | T << 13);
}
不过,我确实喜欢其他发布的解决方案。 以供参考:
// notes from the IEEE-754 specification:
// left to right bits of a binary floating point number:
// size bit ids name description
// ---------- ------------ ---- ---------------------------
// 1 bit S sign
// w bits E[0]..E[w-1] E biased exponent
// t=p-1 bits d[1]..d[p-1] T trailing significant field
// The range of the encoding’s biased exponent E shall include:
// ― every integer between 1 and 2^w − 2, inclusive, to encode normal numbers
// ― the reserved value 0 to encode ±0 and subnormal numbers
// ― the reserved value 2w − 1 to encode +/-infinity and NaN
// The representation r of the floating-point datum, and value v of the floating-point datum
// represented, are inferred from the constituent fields as follows:
// a) If E == 2^w−1 and T != 0, then r is qNaN or sNaN and v is NaN regardless of S
// b) If E == 2^w−1 and T == 0, then r=v=(−1)^S * (+infinity)
// c) If 1 <= E <= 2^w−2, then r is (S, (E−bias), (1 + 2^(1−p) * T))
// the value of the corresponding floating-point number is
// v = (−1)^S * 2^(E−bias) * (1 + 2^(1−p) * T)
// thus normal numbers have an implicit leading significand bit of 1
// d) If E == 0 and T != 0, then r is (S, emin, (0 + 2^(1−p) * T))
// the value of the corresponding floating-point number is
// v = (−1)^S * 2^emin * (0 + 2^(1−p) * T)
// thus subnormal numbers have an implicit leading significand bit of 0
// e) If E == 0 and T ==0, then r is (S, emin, 0) and v = (−1)^S * (+0)
// parameter bin16 bin32
// -------------------------------------------- ----- -----
// k, storage width in bits 16 32
// p, precision in bits 11 24
// emax, maxiumum exponent e 15 127
// bias, E-e 15 127
// sign bit 1 1
// w, exponent field width in bits 5 8
// t, trailing significant field width in bits 10 23
链接地址: http://www.djcxy.com/p/85573.html
上一篇: point in Java