如何标准化尾数

Posted

技术标签:

【中文标题】如何标准化尾数【英文标题】:How to normalize a mantissa 【发布时间】:2015-05-02 06:14:49 【问题描述】:

我正在尝试将int 转换为自定义浮点数,其中用户指定为 exp 和尾数保留的位数,但我不明白转换是如何工作的。我的函数接受一个 int 值和 int exp 来表示数字(值 * 2^exp),即 value = 12,exp = 4,返回 192。但我不明白我需要做些什么来改变这些。几天来我一直在研究这个并使用 IEEE 转换器网络应用程序,但我只是不明白规范化过程是什么。就像我看到它“移动二进制点并调整指数”但我不知道这是什么意思,谁能给我一个例子吗?我也不明白指数偏差是什么。我拥有的唯一信息是您只是在指数中添加了一个数字,但我不明白为什么。我一直在谷歌上搜索一个我能理解的例子,但这对我来说没有任何意义

【问题讨论】:

它是 0.01 --> 1e-2 IOW 的二进制等价物:将尾数右/左移动并将计数加/减到指数。 如果值为 12,并且我们将其设为二进制值,则为 00001100。这需要转移到11000000 x 2^-4,然后我们忘记最左边的位(因为它“总是”1),只是说这是[1]1000000 x 2^-4 你能澄清一下“我不明白我需要做什么来改变这些”的意思吗?你的意思是当你做加法/乘法时你不确定如何改变它们? @eigenchris 就像我如何取十进制 12,并使其成为标准化尾数,然后相应地调整 exp 部分 @U2EF1 那么我怎么知道它需要转移多少次?就像用户为尾数指定 4 位,并且值为 3,我怎么知道将 0011 移到 1000?我可以像 get max_val = pow(2,)-1 然后将 value(0011) 向右移动直到 value > max_val 之类的东西,并让计数器记录我这样做的次数吗? 【参考方案1】:

当我们强制其尾数的整数部分恰好为1 并允许其小数部分为我们喜欢的任何值时,浮点数被标准化

例如,如果我们取数字13.25,即二进制的1101.011101 将是整数部分,01 将是小数部分。

我可以将13.25 表示为1101.01*(2^0),但这不是标准化的,因为整数部分不是1但是,如果我们将指数增加 1,我们可以将尾数右移一位:

  1101.01*(2^0)
= 110.101*(2^1)
= 11.0101*(2^2)
= 1.10101*(2^3)

这种表示1.10101*(2^3)13.25 的规范化形式。


也就是说,我们知道标准化浮点数总是以1.fffffff * (2^exp)

的形式出现

为了提高效率,我们不费心将1 整数部分存储在二进制表示本身中,我们只是假装它在那里。因此,如果我们给您定制的浮点类型 5 位作为尾数,我们会知道位 10100 实际上代表 1.10100

以下是标准 23 位尾数的示例:


至于指数偏差,我们来看看标准的32位float格式,它分为3部分:1个符号位、8个指数位和23个尾数位:

s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm

指数0000000011111111 有特殊用途(比如表示InfNaN),因此使用8 个指数位,我们可以表示254 个不同的指数,比如2^12^254,对于例子。但是如果我们想代表2^-3呢?我们如何得到负指数?

该格式通过自动从指数中减去 127 解决了这个问题。因此:

0000 0001 将是 1 -127 = -126 0010 1101 将是 45 -127 = -82 0111 1111 将是 127-127 = 0 1001 0010 将是 136-127 = 9

这会将指数范围从2^1 ... 2^254 更改为2^-126 ... 2^+127,因此我们可以表示负指数。

【讨论】:

@eipenchris,你能告诉我谁来识别整数部分吗 @kashyap 整数部分是小数点左边的部分(也称为小数点)。【参考方案2】:

Tommy -- chux 和 eigenchris 以及其他人都提供了很好的答案,但是如果我正确地查看了您的 cmets,您似乎仍然在为“我将如何获取此信息”的具体细节而苦苦挣扎然后在创建自定义浮点表示时使用它,其中用户指定指数的位数?”不要难过,前十次经过它,它就像泥巴一样清澈。我想我可以尝试一下清理它。

您熟悉 IEEE754-Single-Precision-Floating-Point 表示:

IEEE-754 Single Precision Floating Point Representation of (13.25)

  0 1 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
 |s|      exp      |                  mantissa                   |

1-bit sign-bit8-bit biased exponent(8 位超 127 表示法)和剩余的23-bit mantissa

当您允许用户选择指数中的位数时,您将不得不修改指数符号以使用新的用户选择的限制。

会发生什么变化?

它会改变sign-bit 的处理方式吗 --

它会改变mantissa 的处理方式吗 -- (您仍将尾数/有效位转换为“隐藏位”格式)。

所以你唯一需要关注的是exponent handling

你会如何处理这个问题?回想一下,当前的 8 位指数采用所谓的 excess-127 表示法(其中 127 表示 7 位的最大值,允许在当前 8-bit 中包含和表达任何偏差限制。如果您的用户选择 6 位作为指数大小,那该怎么办?您将必须提供类似的方法来确保您有一个固定的数字来表示您的新 excess-## 表示法有效在用户范围内。

采用6-bit 用户限制,然后可以尝试选择无偏指数值作为31(可以在5-bits 中表示的最大值)。为此,您可以应用相同的逻辑(以上面的 13.25 为例)。您对数字的二进制表示是 1101.01,您将小数点 3 positions to the left 移到其中得到 1.10101,这给您提供了 3 的指数偏差。

在您的6-bit exponent 情况下,您将添加3 + 31 以获得指数excess-31 notation100010,然后将尾数置于“隐藏位”格式(即从@987654342 中删除前导1 @ 生成新的自定义 Tommy Precision 表示:

IEEE-754 Tommy Precision Floating Point Representation of (13.25)

  0 1 0 0 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
 |s|    exp    |                    mantissa                     |

使用1-bit sign-bit6-bit biased exponent(6 位超31 表示法)和剩余的25-bit mantissa

同样的规则也适用于逆向从上述符号中获取浮点数的过程。 (只是使用31 而不是127 来支持指数的偏差)

希望这在某种程度上有所帮助。如果您真的要允许用户选择的指数大小,我看不出还有什么可以做的。请记住,IEEE-754 标准并不是人们猜测的东西,许多很好的推理和权衡都是为了达到 1-8-23 符号-指数-尾数布局。但是,我认为您的练习在要求您牢牢理解标准方面做得很好。

现在完全迷失了,并且未在此讨论中解决这会对Custom Precision Floating Point Representation 中可以表示的数字范围产生什么影响。我没有看过它,但主要的限制似乎是减少了可以表示的MAX/MIN

【讨论】:

【参考方案3】:

“标准化过程”将输入转换为选择范围。

binary32 期望有效数(不是尾数)在 1.0 <= s < 2.0 范围内,除非该数字具有最小指数。

示例:value = 12, exp = 4value = 12/(2*2*2), exp = 4 + 3value = 1.5, exp = 7 相同

由于有效数字的前导数字始终为1(除非该数字具有最小指数),因此无需存储它。而不是将指数存储为7,而是向其添加了 127 的偏差。

值 = 1.5 十进制 --> 1.1000...000 二进制 --> 0.1000...000 存储二进制(总共 23 位) exp = 7 --> 偏置 exp 7 + 127 --> 134 十进制 --> 10000110 二进制

存储的二进制模式是“符号”、“隐含前导 1 位的有效数字”和“偏置指数”的串联

0 10000110 1000...000 (1 + 8 + 23 = 32 bits)

当偏置指数为0 - 最小值时,隐含位为0,因此可以存储像0.0 这样的小数。

当偏差指数为255 - 最大值时,存储的数据不再代表有限数,而是“无穷大”和“非数字”。

查看参考链接了解更多详情。

【讨论】:

【参考方案4】:

要回答有关“如何在代码中执行此操作”的评论: (假设它是一个 IEEE 浮点数)

A) 从 IEEE 浮点数中提取无符号“指数”和“尾数”。

i) exp = 0x7F800000 & yourFloatVar;

//这从浮点数中获取位 b1 到 b8。 (b0 是有符号位,b9 和 on 是尾数)

ii) exp = exp >> 23; //右移,所以这个指数是右向的

iii) exp += 127; //添加偏差(127 仅适用于 32 位)

iv) mantissa = 0x007FFFFF & yourFloatVar; //从浮点数中取出最后 23 位

B) 标准化

我)

while(true)

    if( ((mantissa & 0xC0000000) != 0x80000000)
         &&((mantissa & 0xC0000000) != 0x40000000) )
    
        mantissa = mantissa << 1;
        exponent--;
    
    else //AKA the float has been normalized
    
        break;
    

如果前导 2 位不是 '01' 或 '10'(这是 2 的补码的属性 - 归一化的条件),则移动尾数并减少指数。

我想指出,这根本不是最有效的算法;我只是想把步骤说清楚。希望我没有错过任何东西!

【讨论】:

【参考方案5】:

要规范化尾数,请将小数点放在最左边的非零数字的左侧

例如

以标准化形式表示 10.11 base 2

= 0.1011 base 2 * 2 的二次方

以二为底是因为您使用的是二进制数,而 +ve 2 的幂是因为您将小数点向左移动了两次。请记住,mantizza 只使用了 4 位

所以 mantizza 将是 1011

【讨论】:

你能举一个更具体的例子来说明这是如何在代码中完成的吗?就像我理解二进制中的 3.1416 将是 11.00100100001111... 所以我需要将其标准化为 1.100100100001111... x 2^1 我得到了抽象部分,但我不明白如何实际实现这个

以上是关于如何标准化尾数的主要内容,如果未能解决你的问题,请参考以下文章

关于IEEE754标准浮点数阶码的移码

关于IEEE754标准浮点数阶码的移码

请问IEEE754标准中,32位所表示的最大正数是多少?老师给的答案是(2-2的-23次方)*2的

IEEE-754浮点数标准

IEEE754是啥

将十进制数表示成ieee754标准的32浮点规格化数 27/64