ノート

[ENHANCEMENT] 8374202: Simplify significand normalization in BigDecimal(double, MathContext) constructor

BigDecimal(double, MathContext) の仮数部の正規化処理を簡潔にしました。

概要#

 BigDecimal(double, MathContext) コンストラクタでは,double の​仮数部​(significand)を​正規化する​処理が​あります1。​ 修正前は,​仮数部が​偶数である​間,​1 ビットずつ右シフトしながら​指数を​ 1​ ずつ増やすループに​なっていました。

while ((significand & 1) == 0) { // i.e., significand is even
    significand >>= 1;
    exponent++;
}

 この​処理は,仮数部の​右端に​連続する​ 0 ビットの​数を​数え,​その​分だけまとめて​シフトすれば​同じ​結果に​なります​(なるらしいです)。​ そこで,Long.numberOfTrailingZeros を​使って​ trailing zero の​数を​求め,​1 回の​シフトと​加算で​正規化する​形に​変更しました2

int nTrailingZeros = Long.numberOfTrailingZeros(significand);
significand >>= nTrailingZeros;
exponent += nTrailingZeros;

 挙動は​維持したまま,​ループを​なくして​処理を​簡潔に​する​変更です。

2026/03/18 JBS で​発見#

 本件を​ JBS で​発見し BigDecimal(double, MathContext) の​実装を​確認しました。​ 対象箇所では,​仮数部の​下位ビットが​ 0 である​限りループを​回して​正規化していました。

 しかし,​Java には​既に​ Long.numberOfTrailingZeros が​あり,​この​用途に​そのまま​使えます。​ significand が​ 0 の​場合は​この​正規化処理に​入る​前に​処理済みである​ため,​ここでは​ trailing zero の​数を​求めて​まとめて​反映できます。

 PR では,ループに​よる​正規化を​ Long.numberOfTrailingZeros に​置き換え,BigDecimal.java 1 ファイルだけを​変更しました3

2026/03/19 レビューコメント#

 Raffaello Giulietti 氏4から,​ほぼ​同じ​提案が​ 2025 年 12 月に​ core-libs-dev へ​送られていた​ものの,​その​時点では​進まなかったと​いう​コメントを​いただきました5

 すでに​近い​提案が​あったと​いう​意味では,​筋の​よい​変更だったのだと​思います。​ 一方で,​既存の​提案が​流れてしまうこともあるので,​小さな​変更でも​ PR と​して​形に​する​ことには​意味が​あると​感じました。

2026/03/27 レビュー#

 Giulietti 氏4に​レビューを​いただき,​Approve されました。​ ただし,​統合前に​ 24 時間​待つよう​コメントを​いただきました。

 ​その​ため,​すぐには​統合せず,​翌日に​ /integrate を​行いました。

2026/03/30 統合#

 Giulietti 氏4に​ /sponsor を​していただいて​ d58fb1e6 と​して​ JDK へ​統合されました。


Footnotes#

  1. 2 3