internal チュートリアル
このページは数値コアのメンテナ向けです。internal のヘルパーをどのように組み合わせるかを示します。10 進の係数をある精度に丸めること、10 進リテラルを厳密な構成要素に構文解析すること、丸められた結果を認証する精緻化ループを書くことを扱います。このパッケージは Luna-Flow/floating の内部パッケージです。例はモジュール内部のコード向けに書かれており、公開パッケージに対してはコンパイルされません。
クイックスタート
モジュール内部で、このパッケージ(およびリテラル用に bigint)をインポートします。
import {
"Luna-Flow/floating/internal",
"moonbitlang/core/bigint",
}
を 2 つのモードで整数に丸めます。
///|
test "rounded quotient" {
let mode_even = @lf_arith.RoundingMode::ToNearestEven
let mode_down = @lf_arith.RoundingMode::TowardZero
inspect(@internal.round_positive_div(2N, 3N, false, mode_even), content="1")
inspect(@internal.round_positive_div(2N, 3N, false, mode_down), content="0")
}
round_positive_div は大きさ と真の商の符号を別々に受け取ります。方向付き丸めモードでは、負の数に対して大きさの丸め方が異なるためです。
日常的なタスク
10 進の係数を p 桁に丸める
桁を超える 10 進数 は、係数を ()で割り、指数に を加えることで丸められます。
///|
fn round_coefficient(
coefficient : BigInt,
exponent : Int,
precision : Int,
mode : @lf_arith.RoundingMode,
) -> (BigInt, Int) {
let negative = coefficient < 0N
let magnitude = @internal.abs_bigint(coefficient)
let excess = @internal.digits10(magnitude) - precision
if excess <= 0 {
return (coefficient, exponent)
}
let rounded = @internal.round_positive_div(
magnitude,
@internal.pow10(excess),
negative,
mode,
)
(if negative { -rounded } else { rounded }, exponent + excess)
}
///|
test "round to three digits" {
let (c, q) = round_coefficient(-123456N, -3, 3, @lf_arith.RoundingMode::TowardNegative)
inspect(c, content="-124")
inspect(q, content="0")
}
桁上がりによって が生じることがあります(例えば )。実際のコアはこの場合を再正規化しますが、このスケッチでは省略しています。
リテラルを厳密な構成要素に構文解析する
split_decimal_string は 10 進パーサのための字句処理を行います。結果を切り詰めると正準な係数が得られます。
///|
test "parse then trim" {
guard @internal.split_decimal_string("-0.012500e2") is Some((negative, digits, exponent)) else {
fail("expected a literal")
}
let coefficient = BigInt::from_string(digits)
let (c, q, dropped) = @internal.trim_trailing_decimal_zeros(coefficient, exponent)
inspect(negative, content="true")
inspect(c, content="125")
inspect(q, content="-2")
inspect(dropped, content="2")
}
したがって です。
精緻化ループで丸めを認証する
認証付き初等関数は Ziv の戦略に従います。ある作業精度で包含区間を計算し、包含区間の両端が同じ数に丸められる場合にのみ丸められた結果を受け入れ、そうでなければ精度を上げます。予算がループの回数を制限します。ここでの「関数」は厳密な比 で、小数部 target ビットに切り下げて丸めます。
///|
fn floor_ratio_certified(
n : BigInt,
d : BigInt,
target : Int,
) -> Result[@internal.CertifiedDyadic, @lf_arith.ArithmeticError] {
let mut budget = @internal.CertifiedRefinementBudget::new(target + 2)
while budget.available() {
let enclosure = match
@internal.certified_dyadic_fraction(n, d, budget.precision()) {
Ok(value) => value
Err(error) => return Err(error)
}
let low = enclosure.lower().round_down(target)
let high = enclosure.upper().round_down(target)
if low.compare(high) == 0 {
return Ok(low)
}
budget = budget.next()
}
Err(
@internal.certified_failure(
"floor_ratio",
@lf_arith.CertificationStage::TargetRounding,
@lf_arith.CertificationFailureReason::RefinementBudgetExhausted,
target,
budget,
),
)
}
///|
test "certified floor of 1/3" {
let third = floor_ratio_certified(1N, 3N, 8).unwrap()
inspect(third.numerator(), content="85")
inspect(third.scale(), content="8")
}
さらに進んで
bin_float、decimal、decimal_gda、ball_floatは、目標精度にガードマージンを加えた精度で精緻化の予算を作成し、予算を使い切るとcertified_failureを報告します。認証付き初等関数については bin_float 設計 を参照してください。semanticは、有限の 2 進数および 10 進数の正準な厳密値としてExactRatを使います。consistencyパッケージは、これらのヘルパーをBigIntのオラクルに対してテストします。- パッケージのテストは、モジュールを含むワークスペースから実行します:
moon test -p Luna-Flow/floating/internal。
よくある落とし穴
- 符号付きの入力。
round_positive_divは分子が負だと中断します。大きさと符号を別々に渡してください。 - のときの
round_shiftは入力をそのまま返します。左シフトは決して行いません。 - ゼロは指数を失います。
remove_factor2、remove_factor10、trim_trailing_decimal_zerosは、係数がゼロの場合に指数 0 を返します。ゼロのコホートが重要な場合は、元の指数を自分で保持してください。 - 正規化されていない 2 進有理数。 スケールの異なる
CertifiedDyadicの値が数値的に等しいことがあります。フィールドではなくcompareで比較してください。
次のステップ
- internal API
- internal 設計
- これらのヘルパーを照合するテストについては consistency チュートリアル。