潜在的な整数の国境を越えた問題に注意してください | JD Logistics 技術チーム

通常の開発プロセスでは、整数の範囲外は見落とされやすい問題です。潜在的な整数の範囲外の問題に注意を払うことで、作成するコードがより堅牢になり、整数の範囲外によって引き起こされるバグを回避できます。問題。

コンパレータ

コードレビューにあるコンパレータの実装は次のとおりです。



一見、コンパレータの実装に問題はありませんが、tag1 = Integer.MIN_VALUE = -2147483648、tag2 が 1 などの 0 より大きい数値の場合、tag1 - tag2 = 2147483647 になりますが、java.util.Comparator によると、 #compare 定義上、tag1 が tag2 より小さい場合、負の数が返されるはずですが、このようなサンプル データに遭遇した場合、上記の書き方ではソート結果が混乱し、関連するバグが発生します。

Spring でのコンパレーターの実装を見てみましょう。Spring では、 Bean の順序を指定するために@Orderアノテーションが提供されています。デフォルト値は Ordered.LOWEST_PRECEDENCE = Integer.MAX_VALUE です。これは、ソート時に最後にランク付けされることを意味します関連するソースコードは次のとおりです。



対応するコンパレータは次のように実装されます。

Integer.compare メソッドが 2 つの整数を比較するために使用されていることがわかります。Integer#compare メソッドのソース コードを表示します。

/**
 * Compares two {@code int} values numerically.
 * The value returned is identical to what would be returned by:
 * <pre>
 *    Integer.valueOf(x).compareTo(Integer.valueOf(y))
 * </pre>
 *
 * @param  x the first {@code int} to compare
 * @param  y the second {@code int} to compare
 * @return the value {@code 0} if {@code x == y};
 *         a value less than {@code 0} if {@code x < y}; and
 *         a value greater than {@code 0} if {@code x > y}
 * @since 1.7
 */
public static int compare(int x, int y) {
    return (x < y) ? -1 : ((x == y) ? 0 : 1);
}

java.lang.Integer#compare は比較に x - y メソッドを使用せず、小なり演算子と等価演算子を使用して直接比較し、潜在的な整数の範囲外の問題を回避していることがわかります最初のコードの正しい実装は return Integer.compare(tag1, tag2) になるはずです。JDK の一般的な数値クラスのソース コードを見ると、それらはすべて、java.lang.Long#compare、java.lang.Double#compare などの静的比較メソッドを提供していることがわかりますが、これらについては説明しません。ここ。

切削率

上記のコードは、あるビジネスロジックで初期カット率を実装したものですが、剰余100モードは比例カットや比例ダウングレードなどのビジネスシーンでよく使われます。上記のコードは、userPin のハッシュ値を使用して余り 100 を取り、それが新しいビジネス ロジックを実行するかどうかを決定するカット率未満であるかどうかを判断します。

/**
 * Returns a hash code for this string. The hash code for a
 * {@code String} object is computed as
 * <blockquote><pre>
 * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
 * </pre></blockquote>
 * using {@code int} arithmetic, where {@code s[i]} is the
 * <i>i</i>th character of the string, {@code n} is the length of
 * the string, and {@code ^} indicates exponentiation.
 * (The hash value of the empty string is zero.)
 *
 * @return  a hash code value for this object.
 */
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

java.lang.String#hashCode は基本的に s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1 を実行することがわかります。文字列] 多項式評価の場合、潜在的なリスクは、計算されたハッシュ値が範囲外になり、 userPin.hashCode() の戻り値が負の数になる可能性があることです (例: "jd_xxxxxxxxxxxx".hashCode() = - 1406647067、Java 言語では、正の数から負の数の法を取得することで負の数を取得することができます。上記のコードのリスクは、予想されるボリューム率を増幅する可能性があることです。上記のコードを使用してオンラインに移行すると、1% のボリューム率を設定すると、1% をはるかに超えるユーザーが新しいビジネスを実行することになります。ロジック (サンプリング ログを通じて、ユーザー ピン セットにおける負の hashCode 値の割合が低くないことがわかりました)、予期しない測定結果につながります。

上記の背景に基づいて、簡単に考えられる修正は、userPin.hashCode の外側の層で Math.abs を使用して、剰余の前の数値が正の数になるようにすることです。

上記の修復ソリューションでは問題はなくなったようですが、完全に正しいという保証はありません。Math.abs のソース コード実装を確認してみましょう。

/**
 * Returns the absolute value of an {@code int} value.
 * If the argument is not negative, the argument is returned.
 * If the argument is negative, the negation of the argument is returned.
 *
 * <p>Note that if the argument is equal to the value of
 * {@link Integer#MIN_VALUE}, the most negative representable
 * {@code int} value, the result is that same value, which is
 * negative.
 *
 * @param   a   the argument whose absolute value is to be determined
 * @return  the absolute value of the argument.
 */
public static int abs(int a) {
    return (a < 0) ? -a : a;
}

コメントで、入力パラメータが Integer.MIN_VALUE、つまり int フィールドの最小値の場合、int フィールドの範囲は次のとおりであるため、戻り値は依然として Integer.MIN_VALUE であることが具体的に述べられていることがわかります。 [-2147483648、2147483647]。JLS の説明に従えば、-x は (~x)+1 と等しくなります。したがって、次のように知ることができます。

x = Integer.MIN_VALUE:
10000000_00000000_00000000_00000000

~x:
01111111_11111111_11111111_11111111

(~x) + 1:
10000000_00000000_00000000_00000000

Magic Lantern で Math.abs を検索すると、この関数に関連する 3 つの記事が見つかります。いずれも Math.abs(Integer.MIN_VALUE) がまだ Integer.MIN_VALUE であることに関連しています。コード レビューの段階でこの問題を発見したとき、私たちは根本的に問題を回避し、バグのあるコードをオンラインに公開しませんでした。最終的に変更された切断率の実装は次のとおりです。



要約する

  • 計算中に整数が範囲外であるため、java.lang.String#hashCode は負の数値を返す場合があります。
  • Java 言語の % は、剰余ではなく剰余です(例: (-21) % 4 = (-21) - (-21) / 4 *4 = -1
  • Math.abs(int a) 入力パラメータが Integer.MIN_VALUE の場合、戻り値は依然として負の数 Integer.MIN_VALUE です。

参考

15.15.4。単項マイナス演算子 -

「mod」と「残り」の違いは何ですか?- スタックオーバーフロー

Java の係数を負の数値に対して適切に動作させるための最良の方法は何ですか? - スタックオーバーフロー

OrderComparator.java · spring-projects/spring-framework

著者: JD Logistics Liu Jianshe Zhang Jiulong Tian Shuang

出典:JD Cloud Developer Community Ziyuanqishuo Tech 転載の際は出典を明記してください

Microsoft、新しい「Windowsアプリ」 .NET 8を正式にGAリリース、最新LTSバージョン XiaomiはXiaomi Velaが完全にオープンソースであり、基盤となるカーネルはNuttXであることを正式に発表 Alibaba Cloud 11.12 障害の原因が明らかに:Access Key Service(アクセスKey) 例外 Vite 5 が正式にリリースされた GitHub レポート : TypeScript が Java に取って代わり、3 番目に人気のある言語になる Rust で Prettier を書き換えるために数十万ドルの報酬を提供 オープンソース作者に「プロジェクトはまだ生きていますか?」と尋ねる 非常に失礼で、失礼な バイトダンス: AI を使用して Linux カーネル パラメータ 演算子を自動的に調整する 魔法の操作: バックグラウンドでネットワークを切断し、ブロードバンド アカウントを無効化し、ユーザーに光モデムの変更を強制する
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/4090830/blog/10148337