【问题标题】:Computing the ranges of floating point data types计算浮点数据类型的范围
【发布时间】:2009-02-09 18:55:15
【问题描述】:

是否可以在不读取 float.h 和使用 ANSI C 的情况下以可移植的方式计算 float、double 和 long double 数据类型的范围?便携是指目标机器不符合 IEEE 754 标准的情况。

我正在阅读 K&R 书,练习 2-1 要求我“计算”它们,所以我认为这意味着完全避免包含 FLT_MIN、FLT_MAX、DBL_MIN 和 DBL_MAX 的 float.h(直接读取这些值肯定不会分类作为“计算”)。

【问题讨论】:

  • 为什么你认为这意味着避免 float.h ?好吧,我认为它希望您使用一些函数,例如 ldexp、FLT_RADIX(或其他任何名称)和东西
  • 好吧,因为 float.h 已经有可用的最小/最大值,在这种情况下看起来像是作弊。但是,是的,float.h 中定义了一些其他常量,它们可能用于确定实际范围。

标签: c


【解决方案1】:

有可能(至少对于 IEEE 754 floatdouble 值)通过(伪代码)计算最大浮点值:

~(-1.0) | 0.5

在进行位旋转之前,我们必须将浮点值转换为整数,然后再转换回来。这可以通过以下方式完成:

uint64_t m_one, half;
double max;

*(double *)(void *)&m_one = -1.0;
*(double *)(void *)&half = 0.5;
*(uint64_t *)(void *)&max = ~m_one | half;

那么它是如何工作的呢?为此,我们必须知道如何对浮点值进行编码。

最高位编码符号,接下来的k 位编码指数,最低位将保存小数部分。对于2 的幂,小数部分为0

指数将以2**(k-1) - 1 的偏差(偏移量)存储,这意味着0 的指数对应于除了最高位之外的所有模式。

有两种具有特殊含义的指数位模式:

  • 如果没有设置位,如果小数部分为零,则值为0;否则,该值是次正规的
  • 如果所有位都已设置,则值为infinityNaN

这意味着最大的正则指数将通过设置除最低位之外的所有位进行编码,如果减去偏差,则对应于 2**k - 22**(k-1) - 1 的值。

对于double 值,k = 11,即最大指数为1023,因此最大浮点值顺序为2**1023,约为1E+308

最大的价值将有

  • 符号位设置为0
  • 除最低指数位以外的所有位都设置为1
  • 所有小数位设置为1

现在,我们可以理解幻数的工作原理了:

  • -1.0 设置了符号位,指数是偏差 - 即除了最高位之外的所有位都存在 - 小数部分是 0
  • ~(-1.0) 仅设置了最高指数位和所有小数位
  • 0.5 有一个符号位和0 的小数部分;指数将是偏差减去1,即除了最高和最低指数位之外的所有位都会出现

当我们通过逻辑或组合这两个值时,我们会得到我们想要的位模式。


该计算也适用于 x86 80 位扩展精度值(又名long double),但必须按字节进行位旋转,因为没有足够大的整数类型来保存 32 位硬件上的值.

偏差实际上并不一定是2**(k-1) - 1 - 只要它是奇数,它就可以用于任意偏差。偏差必须是奇数,否则1.00.5 的指数的位模式将在除最低位之外的其他地方有所不同。

如果浮点类型的基数b(又名基数)不是2,则必须使用b**(-1) 而不是0.5 = 2**(-1)

如果最大指数值不是 reservedrd,请使用 1.0 而不是 0.5。无论基数或偏差如何,这都将起作用(这意味着它不再限于奇数值)。使用1.0的区别在于不会清除最低指数位。


总结一下:

~(-1.0) | 0.5

只要基数为2,偏差为奇数并保留最高指数即可。

~(-1.0) | 1.0

适用于任何基数或偏差,只要不保留最高指数。

【讨论】:

  • 说“for IEEE 754”否定了问题的前提。
  • @Jonathan:至少'至少'并不意味着'仅用于' - 这适用于每个浮点值,偏差为2**(k-1) - 1,保留指数为2**k - 1,其中包括一半和四倍精度值以及 x86 上的 80 位扩展精度值
  • @Jonathan:如果您不限制可能的编码方案,您还想如何“计算”一些东西?只是没有适用于所有编码的算法!
【解决方案2】:

对于所有应用程序的 99.99%,您应该假设 IEEE 754 并使用<float.h> 中定义的常量。在另外 0.01% 中,您将使用非常专业的硬件,在这种情况下,您应该知道根据硬件使用什么。

【讨论】:

    【解决方案3】:

    冒着多余答案的风险:

    没有。没有可移植的方法来计算范围。这就是提供 <float.h> 标头的原因 - 因为没有可移植的方法来获取其中包含的信息。

    【讨论】:

    • 确实如此 - 但有些计算适用于各种编码方案!
    【解决方案4】:

    你可以试着让一个浮动更大直到它溢出。

    【讨论】:

    • 这就是我一直在用整数类型做的事情——但不是需要相当长的时间来溢出一个 long double 吗?
    • 如果你通过指数搜索来做,它会很快溢出。您甚至可以使数字增长得更快(例如,最大指数的指数搜索与最大值的指数搜索)
    猜你喜欢
    • 1970-01-01
    • 2020-10-22
    • 1970-01-01
    • 2018-01-25
    • 2020-10-31
    • 1970-01-01
    • 2012-01-14
    • 1970-01-01
    • 2018-07-01
    相关资源
    最近更新 更多