如何标准化尾数
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
并允许其小数部分为我们喜欢的任何值时,浮点数被标准化。
例如,如果我们取数字13.25
,即二进制的1101.01
,1101
将是整数部分,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
指数00000000
和11111111
有特殊用途(比如表示Inf
和NaN
),因此使用8 个指数位,我们可以表示254 个不同的指数,比如2^1
到2^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-bit
、8-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 notation
:100010
,然后将尾数置于“隐藏位”格式(即从@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-bit
、6-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 = 4
与value = 12/(2*2*2), exp = 4 + 3
value = 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 我得到了抽象部分,但我不明白如何实际实现这个以上是关于如何标准化尾数的主要内容,如果未能解决你的问题,请参考以下文章