Javaの四則演算クラスにdouble型の演算メソッドを追加してしまった。
これにより、今までlong型の演算メソッド名が「eval()」だったのを「evalLong()」に改名。ただ、Javaの一般的な命名ではあまり単語を省略しないから、「evaluateLong()」の方が良かったかな…?
基本的に引数のlongをdoubleに変えたメソッドを増やすだけだから、クラスの数が多い分面倒なだけだと思っていたら、意外なところで詰まってしまった。
doubleって、ビット演算できないんだー?!…改めて考えてみれば、どんな演算をするんだって気にもなるけど…。
例えば「0.5」のビット否定って…? なんと言っても「浮動」小数だから、桁をどこに置くかが値が全然変わってしまう。
理想は「整数を評価するとlong型での評価と同じ値になる計算式」であり、いくつか案を検討してみた。
●案1「2の補数表現」
2の補数表現を応用してみても、浮動小数には使えないだろうなー。
-n ≡ ~n + 1
↓
~n ≡ -n - 1
絶対、小数は変になりそう(爆)
●案2「浮動小数のビット表現」
long bits = Double.doubleToLongBits(n);
n = Double.longBitsToDouble(~bits);
浮動小数のビット表現をそのまま反転すると、符号ビットも指数も値も全部反転してしまう(爆)
●案3「固定小数」
整数部と小数部をそれぞれlong型に代入して計算。
これだと一見良さそうな感じがするけど、Javaのlongって符号有りだから、整数部と小数部を結合するのが難しい。
long 整数部 = (long)n;
long 小数部 = (n - 整数部) * Math.pow(2,63); //doubleは<<が使えない
整数部 = ~整数部; 小数部 = ~小数部
n = 整数部 + (double)小数部 / Math.pow(2,63); //doubleは|や>>が使えない
大抵の場合、ビット反転した小数部は負の値になるよね。整数部と足し算したら絶対おかしくなる(爆) 本来は小数部を右シフト(符号なし)して論理和で整数部とつなぎたいところ。
しかも浮動小数は指数部の最大値が大きいから、小数部64ビットくらいでは足りないんじゃないかなー。ビット否定ならともかく、論理積や論理和なんかを計算しようと思ったら2つの値の指数部が異なる可能性が高いし、考えるのも嫌だー!
指数部が11ビットあると、固定小数に直すと…何ビットあれば足りるんだろうな~。完璧に展開できれば、それが一番正確な演算結果になるんだろうけど…時間も資源も無駄に必要そうだ(汗)
●案4「あきらめる」
Windowsの『電卓』だと、ビット演算は素直に小数部を無視している。
どうせdoubleでビット演算をする人なんていない(Javaでもエラーになる)んだから、手を抜くことに決定!(爆)
n = ~(long)n;