繁体   English   中英

找到大于double值的最小浮点数

[英]Find minimum float greater than a double value

我有与CUDNN_BN_MIN_EPSILON值在被使用的问题cudnnBatchNormalizationForwardTraining功能(参见该文档在这里 ),而且事实证明那是因为我是路过的float1e-5f而不是双(我的工作float值保存内存并加快计算速度),这个值一旦转换为float,就会略小于1e-5 ,这是该常量的实际值。

经过一些反复试验,我发现我现在正在使用一个不错的近似值:

const float CUDNN_BN_MIN_EPSILON = 1e-5f + 5e-13f;

我确信有更好的方法可以解决这样的问题,所以问题是:

给定一个正的double值,找到最小可能float值的最佳(如“可靠”)方式是什么(单独使用以及if / when转换为double )是否严格大于初始double值?

另一种形成这个问题的方法是,给定doubled1floatf1d1 - (float)f1应该是最小可能的负值 (否则它意味着f1 小于 d1,这不是我们正在寻找什么)。

我做了一些基本的试验和错误(使用1e-5作为我的目标值):

// Check the initial difference
> 1e-5 - 1e-5f
2,5262124918247909E-13 // We'd like a small negative value here

// Try to add the difference to the float value
> 1e-5 - (1e-5f + (float)(1e-5 - 1e-5f))
2,5262124918247909E-13 // Same, probably due to approximation

// Double the difference (as a test)
> 1e-5 - (1e-5f + (float)((1e-5 - 1e-5f) * 2))
-6,5687345259044915E-13 // OK

使用此近似值,最终float值为1,00000007E-05 ,看起来很好。

但是* 2乘法在我的结尾完全是任意的,我不确定它是可靠的还是在那里做的最佳可能的事情。

有没有更好的方法来实现这一目标?

谢谢!


编辑 :这是我现在使用的(坏)解决方案,很乐意用更好的解决方案替换它!

/// <summary>
/// Returns the minimum possible upper <see cref="float"/> approximation of the given <see cref="double"/> value
/// </summary>
/// <param name="value">The value to approximate</param>
public static float ToApproximatedFloat(this double value)
    => (float)value + (float)((value - (float)value) * 2);

解决方案 :这是最终正确的实施(感谢John Bollinger):

public static unsafe float ToApproximatedFloat(this double value)
{
    // Obtain the bit representation of the double value
    ulong bits = *((ulong*)&value);

    // Extract and re-bias the exponent field
    ulong exponent = ((bits >> 52) & 0x7FF) - 1023 + 127;

    // Extract the significand bits and truncate the excess
    ulong significand = (bits >> 29) & 0x7FFFFF;

    // Assemble the result in 32-bit unsigned integer format, then add 1
    ulong converted = (((bits >> 32) & 0x80000000u)
                        | (exponent << 23)
                        | significand) + 1;

    // Reinterpret the bit pattern as a float
    return *((float*)&converted);
}

在C:

#include <math.h>

float NextFloatGreaterThan(double x)
{
    float y = x;
    if (y <= x) y = nexttowardf(y, INFINITY);
    return y;
}

如果你不想使用库例程,那么用-NextBefore(-y)替换nexttowardf(y, INFINITY) -NextBefore(-y) ,其中NextBefore取自这个答案并修改:

  • 更改doublefloatDBL_FLT_
  • .625更改为.625f
  • SmallestPositive < fabs(q)*Scale ? fabs(q)*Scale : SmallestPositive替换fmax(SmallestPositive, fabs(q)*Scale) SmallestPositive < fabs(q)*Scale ? fabs(q)*Scale : SmallestPositive SmallestPositive < fabs(q)*Scale ? fabs(q)*Scale : SmallestPositive
  • (q < 0 ? -q : q)替换fabs(q) (q < 0 ? -q : q)

(显然,例程可以从-NextBefore(-y)转换为NextAfter(y) 。这留给读者练习。)

因为您似乎对表示级别的详细信息感兴趣,所以您将依赖于floatdouble类型的表示。 然而,在实践中,很可能归结为IEEE-754的基本“binary32”和“binary64”格式。 它们具有一个符号位的一般形式,几个偏置指数位和一组有效位,包括对于归一化值,一个有效位的隐含位。

简单的案例

鉴于IEEE-754二进制64格式的double ,其值不低于+ 2-126 ,您想要做的是

  • 以可以直接检查和操作的形式获得原始double值的位模式。 例如,作为无符号的64位整数。

     double d = 1e-5; uint64_t bits; memcpy(&bits, &d, 8); 
  • 提取并重新偏置指数字段

     uint64_t exponent = ((bits >> 52) & 0x7FF) - 1023 + 127; 
  • 提取有效位并截断多余的位

     uint64_t significand = (bits >> 29) & 0x7fffff; 
  • 以32位无符号整数格式汇总结果

     uint32_t float_bits = ((bits >> 32) & 0x80000000u) | (exponent << 23) | significand; 
  • 加一个。 由于您希望结果严格大于原始double ,因此无论所有截断的有效位数是否为0,这都是正确的。如果加法溢出有效位,则将正确递增指数字段。 然而,它可能产生无穷大的位模式。

     float_bits += 1; 
  • 将位模式存储/复制/重新解释为float

     float f; memcpy(&f, &float_bits, 4); 

负数

给定binary64格式的负double ,其幅度不小于2 -126 ,遵循上述过程,除了从float_bits减1而不是添加1。 请注意,对于-2 -126 ,这会产生一个二次正常的二进制32(见下文),这是正确的结果。

零和非常小的数字,包括次正规数

IEEE 754提供了非零数量非零数量的精度低的表示。 这种表示称为次正规 在某些情况下,超过给定输入binary64的最小二进制32是一个次正规,包括一些不是二进制64次正规的输入。

此外,IEEE 754提供带符号的零,-0是一种特殊情况:严格大于-0(任一格式)的最小二进制32是最小的正次正规数。 注意:不是+0,因为根据IEEE 754,+ 0和-0通过正常的比较运算符进行比较。 最小正,非零,次正规binary32值具有位模式0x00000001。

受这些考虑因素影响的二进制64值具有偏置的二进制64指数字段,其值小于或等于二进制64指数偏差和二进制32指数偏差之间的差值(896)。 这包括具有精确为0的有偏差指数的那些,其表征二进制64零和子正规。 在简单案例程序中检查重组步骤应该使您正确地得出结论,该程序将对这些输入产生错误的结果。

这些案件的代码留作练习。

无穷大和NaN

具有偏置的二进制64指数字段集的所有位的输入表示正或负无穷大(当binary64有效数字没有设置位时)或非数字(NaN)值。 Binary64 NaNs和正无穷大应该转换为它们的binary32当量。 负无穷大可能应该转换为最大量级的负二进制32值。 这些需要作为特殊情况处理。

这些案件的代码留作练习。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM