a/bをする場合、bが2の乗数であれば「aの指数 - log2(b)」で除算ができます。
#include <iostream>
#include <bitset>
#include <cstdint>
#include <cassert>
#include <cmath>
union SingleFloat {
float value;
struct {
int fraction : 23;
int exponent : 8;
bool sign : 1;
} parts;
};
SingleFloat divide(SingleFloat a, std::uint32_t b)
{
assert(std::bitset<32>(b).count() == 1);
SingleFloat result;
result.parts.fraction = a.parts.fraction;
result.parts.exponent = a.parts.exponent - std::log2(b);
result.parts.sign = a.parts.sign;
return result;
}
SingleFloat make_by_float(float x)
{
SingleFloat result;
result.value = x;
return result;
}
int main()
{
std::cout << divide(make_by_float(8.0f), 4).value << std::endl;
std::cout << divide(make_by_float(9.0f), 4).value << std::endl;
std::cout << divide(make_by_float(24.0f), 8).value << std::endl;
}
出力:
2
2.25
3
ここでは、a
が0、非正規化数、無限大、NaNの場合については考慮していません。
追記(2016/12/27 17:45)
もうちょっと真面目に実装し、上述した未考慮のケースに対応したコードはこちら:
#include <iostream>
#include <bitset>
#include <cstdint>
#include <cassert>
#include <cmath>
union SingleFloat {
float value;
struct {
int fraction : 23;
int exponent : 8;
bool sign : 1;
} parts;
};
SingleFloat make_by_float(float x)
{
SingleFloat result;
result.value = x;
return result;
}
SingleFloat divide(SingleFloat a, std::uint32_t b)
{
assert(std::bitset<32>(b).count() == 1);
if (std::isnan(a.value))
return make_by_float(a.value);
if (std::isinf(a.value))
return make_by_float(a.value);
int exponent_x = a.parts.exponent;
int exponent_y = std::log2(b);
if (exponent_x <= exponent_y) {
return make_by_float(0.0f);
}
else {
SingleFloat result;
result.parts.fraction = a.parts.fraction;
result.parts.sign = a.parts.sign;
result.parts.exponent = exponent_x - exponent_y;
return result;
}
}
int main()
{
std::cout << " 8/4 = " << divide(make_by_float(8.0f), 4).value << std::endl;
std::cout << " 9/4 = " << divide(make_by_float(9.0f), 4).value << std::endl;
std::cout << "24/8 = " << divide(make_by_float(24.0f), 8).value << std::endl;
std::cout << " 0/2 = " << divide(make_by_float(0.0f), 2).value << std::endl;
std::cout << "-0/2 = " << divide(make_by_float(-0.0f), 2).value << std::endl;
using limits = std::numeric_limits<float>;
std::cout << "denorm_min/2 = " << divide(make_by_float(limits::denorm_min()), 2).value << std::endl;
std::cout << "NaN/2 = " << divide(make_by_float(limits::quiet_NaN()), 2).value << std::endl;
std::cout << "inf/2 = " << divide(make_by_float(limits::infinity()), 2).value << std::endl;
std::cout << "-inf/2 = " << divide(make_by_float(-limits::infinity()), 2).value << std::endl;
}
出力:
8/4 = 0
9/4 = 0
24/8 = 0
0/2 = 0
-0/2 = 0
denorm_min/2 = 0
NaN/2 = nan
inf/2 = inf
-inf/2 = -inf
参照
- 2018/03/01 11:51 : コメント欄での指摘を受け、
-limits::quiet_NaN()
のように負数のNaNを使用していたところの符号を削除