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) { // 2の乗数のみ受け付ける 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() { // 8/4 == 2 std::cout << divide(make_by_float(8.0f), 4).value << std::endl; // 9/4 == 2.25 std::cout << divide(make_by_float(9.0f), 4).value << std::endl; // 24/8 == 3 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) { // 2の乗数のみ受け付ける 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) { // 0や非正規化数のように指数が小さいaをなにかの値で割るとアンダーフローするので、0を返す 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を使用していたところの符号を削除