C言語の数学関数の floor
を自前で実装するにはどうするか。
floor関数
floor
関数は日本語だと「床関数」、与えられた浮動小数点数を超えない最大の整数を返す関数で、math.h
で宣言されている:
double floor(double x); |
実装に入る前にdouble
がプロセッサ上でどのように表現されているかについて。
浮動小数点数の内部表現(double)
浮動小数点数をコンピュータ内部でどうやって扱っているかというと、大抵の場合IEEE 754という形式で扱っている。
double
の場合、全体で64ビット長で、符号部1、指数部11、仮数部52となる。
- 仮数部は0.5以上1.0未満に正規化され、最上位ビットが省略される
- 指数部は1022(
= 0x3fe
)底上げされている 0.0
や±Infinity
やNaN
は特別扱い
例えば1.0
は、0x3ff_0000000000000
(0.5 x 2 ^ 1)となる。
floor関数の実装方法
double
を扱う関数になるが、浮動小数点数演算ではなくビット演算で処理する。
でひとまず正の場合のみを考える。
仮数部に対する小数点の位置は ((52+1) - 指数) となる。
実際には指数は1022底上げされているので、指数部をe
とすると(52+1) - (e - 1022)
となる。
それで小数点の位置によって場合わけ:
- 仮数部にかかる場合(
1022 < e <= 1074
):小数部をビットマスクで落とす - 仮数部より上になる場合(
e <= 1022
):値が小さくて元々整数部がないので、0.0
を返す - 仮数部より下になる場合(
e > 1074
):値が大きくて元々小数部がないので、元の値を返す
入力が0.0
の場合、e = 0
で条件2となるが、一応-0.0
を考慮して元の値を返すことにする。
入力がNaN
または±Infinity
の場合、e = 0x7ff
で条件3で元の値が返されるのでOK。
入力が負の場合
値が負の場合は、符号を無視して仮数部を見た場合に小数となる部分が0じゃない場合は切り上げてやる。
ソース
というわけで、実装したソースは floor.c
|
ceil関数
ceil
関数も、floor
の正負を逆にして同様に:ceil.c
雑感
double
の値のビット表現を取り出すためにC言語ではint64_t
を指すポインタ経由で取り出す必要があって、それがいいのかどうか。
それなりによく使いそうなもんだしそんなに回路複雑にならなそうだけど、プロセッサの命令として実装されてないだろうか?