【发布时间】:2019-10-14 22:46:58
【问题描述】:
为了好玩并想了解更多关于浮点数的工作原理,我正在尝试创建一个函数,该函数接受两个单精度浮点数并将它们相加。
到目前为止,我所做的对于相同符号的数字非常有效,但是当数字具有相反的符号时,它就会分崩离析。我查看了许多问题和网站(UAF、How do you add 8-bit floating point with different signs、ICL、Adding 32 bit floating point numbers.、How to add and subtract 16 bit floating point half precision numbers?、How to subtract IEEE 754 numbers?),但那些提出减法的问题大多将其描述为“基本相同,但要减去”,我发现这不是很有帮助。 UAF 确实说
通过首先转换为 2 的补码然后执行加法来处理负尾数。加法完成后,将结果转换回符号大小形式。
但我似乎不知道该怎么做。我发现 this 和 this 解释了有符号幅度是什么以及如何在它和二进制补码之间进行转换,所以我尝试像这样进行转换:
manz = manx + ( ( (many | 0x01000000) ^ 0x007FFFFF) + 1);
像这样:
manz = manx + ( ( (many | 0x01000000) ^ 0x007FFFFF) + 1);
manz = ( ((manz - 1) ^ 0x007FFFFF) & 0xFEFFFFFF);
但这些都不起作用。
尝试了其他来源描述的减法方法,我尝试以如下各种方式否定负数的尾数:
manz = manx - many;
manz = manx + (many - (1<<23));
manz = manx + (many - (1<<24));
manz = manx + ( (many - (1<<23)) & 0x007FFFFF );
manz = manx + ( (many - (1<<23)) + 1);
manz = manx + ( (~many & 0x007FFFFF) + 1);
manz = manx + (~many + 1);
manz = manx + ( (many ^ 0x007FFFFF) + 1);
manz = manx + ( (many ^ 0x00FFFFFF) + 1);
manz = manx + ( (many ^ 0x003FFFFF) + 1);
这是应该根据符号处理加法的语句,它是在尾数对齐之后:
expz = expy;
if(signx != signy) { // opp sign
if(manx < many) {
signz = signy;
manz = many + ((manx ^ 0x007FFFFF) + 1);
} else if(manx > many) {
signz = signx;
manz = manx - ((many ^ 0x007FFFFF) + 1);
} else { // x == y
signz = 0x00000000;
expz = 0x00000000;
manz = 0x00000000;
}
} else {
signz = signx;
manz = manx + many;
}
这是紧随其后的代码,它在溢出的情况下对数字进行规范化,当它们具有相同的符号时它可以工作,但我不确定它在减法时的工作方式是否有意义:
if(manz & 0x01000000) {
expz++;
manz = (manz >> 1) + (manz & 0x1);
}
manz &= 0x007FFFFF;
使用测试值-3.34632F 和34.8532413F,当它应该是0x41FC0E2D (31.506922) 和测试值3.34632F 和@ 时,我得到了答案0x427E0716 (63.506920) 987654342@,当它应该是0xC1FC0E2D (-31.506922) 时,我得到了答案0xC27E0716 (-63.506920)。
我能够通过改变减法时对浮点数进行标准化的方式来解决我的问题。
expz = expy;
if(signx != signy) { // opp sign
if(manx < many) {
signz = signy;
manz = many - manx;
} else if(manx > many) {
signz = signx;
manz = manx - many;
} else { // x == y
signz = 0x00000000;
expz = 0x00000000;
manz = 0x00000000;
}
// Normalize subtraction
while((manz & 0x00800000) == 0 && manz) {
manz <<= 1;
expz--;
}
} else {
signz = signx;
manz = manx + many;
// Normalize addition
if(manz & 0x01000000) {
expz++;
manz = (manz >> 1) + ( (x & 0x2) ? (x & 0x1) : 0 ); // round even
}
}
manz &= 0x007FFFFF;
【问题讨论】:
-
printf("%f\n", -3.34632F + 34.8532413F);打印 31.506922 -
很抱歉,我真的看不出问题所在。如果您有两个浮点数
a和b。为什么不直接使用a+b来添加它们? -
因为没有显示如何加法的工作原理,我想准确地弄清楚加法是如何执行的,而不仅仅是添加两个浮点数。
-
@klutt 因为任务是通过了解存储格式来分解两个
floats 并创建一个新的来做总和。
标签: c algorithm floating-point