Для развлечения и чтобы узнать больше о том, как работают поплавки, я пытаюсь создать функцию, которая берет два поплавка одинарной точности и складывает их вместе.
То, что я сделал до сих пор, работает отличнодля тех же чисел знака, но он разваливается, когда числа имеют противоположные знаки. Я просмотрел несколько вопросов и сайтов ( UAF , Как добавить 8-битную плавающую точку с разными знаками , ICL , Добавление 32-разрядных чисел с плавающей запятой. , Как сложить и вычесть 16-разрядные числа половинной точности с плавающей запятой? , Как вычесть числа IEEE 754? ), но те, которыеПоднимите вычитание, в основном опишите его как «в основном то же самое, но вместо этого вычтите», что я не нашел чрезвычайно полезным. 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
я получаю ответ 0x427E0716
(63.506920
), когда он должен быть 0x41FC0E2D
(31.506922
), а с тестовыми значениями 3.34632F
и -34.8532413F
, Iполучить ответ 0xC27E0716
(-63.506920
), когдаэто должно быть 0xC1FC0E2D
(-31.506922
).
Я смог исправить свою проблему, изменив способ, которым я нормализовал поплавки при вычитании.
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;