- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
为了好玩并进一步了解 float 的工作原理,我尝试制作一个函数,它接受两个单精度 float ,并将它们相加。
到目前为止,我所做的对于相同符号的数字非常有效,但当数字具有相反的符号时,它就会分崩离析。我查看了许多问题和网站( 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 确实说
Negative mantissas are handled by first converting to 2's complement and then performing the addition. After the addition is performed, the result is converted back to sign-magnitude form.
但我似乎不知道该怎么做。我找到了 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
,我得到答案 0x427E0716
(63.506920
)当它应该是 0x41FC0E2D
(31.506922
) 和测试值 3.34632F
和 -34.8532413F
时,我得到答案 0xC27E0716
(-63.506920
) 而它应该是 0xC1FC0E2D
(-31.506922
)。
我能够通过更改减法时对 float 进行标准化的方式来解决我的问题。
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;
最佳答案
How to add two floating point numbers with opposite sign?
大多数情况下你不会。
对于不能依赖“二进制补码溢出”的数值类型的所有工作(例如 float 、大数库……),你总是以这样的方式结束:
add_signed(v1, v2) {
if( v1 < 0) {
if( v2 < 0) {
// Both negative
return -add_unsigned(-v1, -v2);
} else {
// Different sign, v1 is negative
return subtract_unsigned(v2, -v1);
}
} else {
if( v2 < 0) {
// Different sign, v2 is negative
return subtract_unsigned(v1, -v2);
} else {
// Both positive
return add_unsigned(v1, v2);
}
}
}
subtract_signed(v1, v2) {
return add_signed(v1, -v2);
}
add_unsigned(v1, v2) {
// Here we know that v1 and v2 will never be negative, and
// we know that the result will never be negative
...
}
subtract_unsigned(v1, v2) {
if(v1 < v2) {
return -subtract_unsigned(v2, v1);
}
// Here we know that v1 and v2 will never be negative, and
// we know that the result will never be negative
...
}
换句话说;所有实际加法和所有实际减法都发生在无符号(“从不负”)数字上。
仅添加 32 位浮点仿真的更完整示例(在 C 中,未经测试且可能有错误,可能适用于非正规数,也可能不适用于非正规数,不支持“NaN/s”或无穷大,不支持上溢或下溢,没有“左移尾数以减少舍入前的精度损失”,并且不支持与“向零舍入”不同的舍入模式):
#define SIGN_FLAG 0x80000000U
#define EXPONENT_MASK 0x7F800000U
#define MANTISSA_MASK 0x007FFFFFU
#define IMPLIED_BIT 0x00800000U
#define OVERFLOW_BIT 0x01000000U
#define EXPONENT_ONE 0x00800000U
uint32_t add_signed(uint32_t v1, uint32_t v2) {
if( (v1 & SIGN_FLAG) != 0) {
if( (v2 & SIGN_FLAG) != 0) {
// Both negative
return SIGN_FLAG | add_unsigned(v1 & ~SIGN_FLAG, v2 & ~SIGN_FLAG);
} else {
// Different sign, v1 is negative
return subtract_unsigned(v2, v1 & ~SIGN_FLAG);
}
} else {
if( (v2 & SIGN_FLAG) != 0) {
// Different sign, v2 is negative
return subtract_unsigned(v1, v2 & ~SIGN_FLAG);
} else {
// Both positive
return add_unsigned(v1, v2);
}
}
}
uint32_t subtract_signed(uint32_t v1, uint32_t v2) {
return add_signed(v1, v2 ^ SIGN_FLAG);
}
uint32_t add_unsigned(uint32_t v1, uint32_t v2) {
// Here we know that v1 and v2 will never be negative, and
// we know that the result will never be negative
if(v1 < v2) { // WARNING: Compares both exponents and mantissas
return add_unsigned(v2, v1);
}
// Here we know the exponent of v1 is not smaller than the exponent of v2
uint32_t m1 = (v1 & MANTISSA_MASK) | IMPLIED_BIT;
uint32_t m2 = (v2 & MANTISSA_MASK) | IMPLIED_BIT;
uint32_t exp2 = v2 & EXPONENT_MASK;
uint32_t expr = v1 & EXPONENT_MASK;
while(exp2 < expr) {
m2 >>= 1;
exp2 += EXPONENT_ONE;
}
uint32_t mr = m1+m2;
if( (mr & OVERFLOW_BIT) != 0) {
mr >> 1;
expr += EXPONENT_ONE;
}
return expr | (mr & ~IMPLIED_BIT);
}
uint32_t subtract_unsigned(uint32_t v1, uint32_t v2) {
if(v1 == v2) {
return 0;
}
if(v1 < v2) {
return SIGN_FLAG ^ subtract_unsigned(v2, v1);
}
// Here we know the exponent of v1 is not smaller than the exponent of v2,
// and that (if exponents are equal) the mantissa of v1 is larger
// than the mantissa of v2; and therefore the result will be
// positive
uint32_t m1 = (v1 & MANTISSA_MASK) | IMPLIED_BIT;
uint32_t m2 = (v2 & MANTISSA_MASK) | IMPLIED_BIT;
uint32_t exp2 = v2 & EXPONENT_MASK;
uint32_t expr = v1 & EXPONENT_MASK;
while(exp2 < expr) {
m2 >>= 1;
exp2 += EXPONENT_ONE;
}
uint32_t mr = m1-m2;
while( (mr & IMPLIED_BIT) == 0) {
mr <<= 1;
expr -= EXPONENT_ONE;
}
return expr | (mr & ~IMPLIED_BIT);
}
关于c - 如何将两个符号相反的 float 相加?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58384951/
我是一名优秀的程序员,十分优秀!