浮動小数点演算に関する別のトピックです。
私はこれに頭を悩ませようとしてきましたが、負の浮動小数点値を追加してもうまくいかない理由がわかりません。
それらが両方とも正の値である場合、すべてが期待どおりに機能しています (これは結局のところ浮動小数点であるため、期待されたものから大きく外れていない数値を返します)。
明らかではない場合に備えて、32ビットバージョンを使用しています;)
私はすでにこれを読んでいます:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html そして、オンラインで浮動小数点演算のさまざまな素晴らしい説明を見つけました - しかし、私はまだ正と負の混合値 (または以下のコードのケース 2、3、および 4) を追加する方法を理解できないようです。
これまでの私のコードは次のとおりです。
int flt32_get_sign (flt32 x) {
/** shift sign bit right; 0 = pos, 1 = neg */
return ((x & 0x80000000) >> 31);
}
int flt32_get_exp (flt32 x) {
/** get biaseed exponent value */
return ((x & 0x7F800000) >> 23);
}
int flt32_get_val (flt32 x) {
/** mask off mantissa
* make sure implicit one set
*/
return ((x & 0x7FFFFF) ^ 0x800000);
}
int flt32_left_most_1 (int bits) {
int position = -1;
/** make sure working with abs value */
if (flt32_get_sign(bits) != 0){
bits = flt32_negate(bits);
}
while(bits != 0){
position++, bits >>=1;
}
return position;
}
int flt32_right_most_1 (int bits) {
int position = -1;
/** make sure working with abs value */
if (flt32_get_sign(bits) != 0){
bits = flt32_negate(bits);
}
while (!(bits & 1)){
position++, bits >>=1;
}
return position;
}
flt32 flt32_abs (flt32 x) {
return (x & 0x7FFFFFFF);
}
flt32 flt32_negate (flt32 x) {
if (flt32_get_sign(x) == 0){
/** is possitive */
return (x ^ 0x80000000);
}
/** else is negative */
return (x & 0x7FFFFFFF);
}
flt32 flt32_add (flt32 x, flt32 y) {
/**
* Possible casses:
* 1: +x +y; 2: +x -y; 3: -x +y; 4: -x -y
*/
flt32 sum, x_val, y_val;
int shift;
/** Case 1 */
if (flt32_get_sign(x) == 0 && flt32_get_sign(y) == 0){
if (flt32_get_exp(x) == flt32_get_exp(y)){
/** no shifting neccesary
* add mantissa's then mask to make sure
* we don't get overflow into the exponent bits
* then add exponent back to new value
*/
sum = (x & 0x7F800000) + ((flt32_get_val(x) + flt32_get_val(y)) & 0x7FFFFF);
} else if (flt32_get_exp(x) > flt32_get_exp(y)){
/** exponent of x is larger than y
* need to right shift y and set its exponent = exponent of x
*/
shift = (flt32_get_exp(x) - flt32_get_exp(y));
y_val = flt32_get_exp(x) + (flt32_get_val(y) >> shift);
sum = x + y_val;
} else {
/** exponent x is smaller than y
* need to right shift x and set its exponent = exponent of y
*/
shift = (flt32_get_exp(y) - flt32_get_exp(x));
x_val = flt32_get_exp(y) + (flt32_get_val(x) >> shift);
sum = x_val + y;
}
}
/** Case 2 */
if (flt32_get_sign(x) == 0 && flt32_get_sign(y) == 1){
if (flt32_get_exp(x) == flt32_get_exp(y)){
/** no shifting neccesary
* add mantissa's then mask to make sure
* we don't get overflow into the exponent bits
* then add exponent back to new value
*/
x_val = ((x & 0xFF800000) + flt32_get_val(x));
y_val = ((y & 0xFF800000) + flt32_get_val(y));
sum = x_val + flt32_negate(y_val);
} else if (flt32_get_exp(x) > flt32_get_exp(y)){
/** exponent of x is larger than y
* need to right shift y and set its exponent = exponent of x
*/
shift = (flt32_get_exp(x) - flt32_get_exp(y));
y_val = flt32_get_exp(x) + (flt32_get_val(y) >> shift);
sum = x + flt32_negate(y_val);
} else {
/** exponent x is smaller than y
* need to right shift x and set its exponent = exponent of y
*/
shift = (flt32_get_exp(y) - flt32_get_exp(x));
x_val = flt32_get_exp(y) + (flt32_get_val(x) >> shift);
sum = x_val + flt32_negate(y);
}
}
return sum;
}
余談ですが、これらすべてに頭を悩ませているときに私が行った観察にすぎません。浮動小数点を理解することは素晴らしいことであり、必要でさえあるように思えますが、私が遭遇したほぼすべての記事は、教科書でさえも、可能な限りそれを避けるように言っています! :)