Introduction to safe programming with numeric library / 数値ライブラリで始める安全なプログラミング
Kushiro Taichi
June 08, 2024
220
Calculating charges is a universal concern in application, accuracy of numeric type is critical. This session introduce practice of typelevel/spire to solve complex calculations where formula, rounding, serialization are applied.
料金計算はアプリケーションにおける普遍的な関心事であり数値型の精度の考慮が重要です。私達のチームでは複雑な料金計算において段階的に計算式・端数処理・シリアライズを適用するケースに対して、typelevel/spireを導入し課題の解決にトライしています。本セッションでは実例を元に数値をより安全に扱う方法を紹介し、最小限のテストでバグの少ないプログラミングを明日から実践できるようにします。
Kushiro Taichi
June 08, 2024
More Decks by Kushiro Taichi
boykush
2
210
boykush
640
boykush
480
boykush
38
Other Decks in Programming
uchihara
2
700
logica0419
150
mokoshi
110
kota_syan
5.6k
replu
2
790
maaaato
2
500
abekoh
6
730
hpgrahsl
120
gunnarmorling
1
130
Featured
notwaldorf
77
5.3k
chriscoyier
501
140k
addyosmani
504
110k
mongodb
34
8.9k
denniskardys
228
130k
soudai
138
42k
philnash
40
2.6k
andyhume
74
5.8k
sonatard
57
9.4k
afnizarnur
188
16k
morganepeng
117
18k
brettharned
110
5.6k
Transcript
-
Introduction to safe programming with numeric library ScalaMatsuri 2024 @boykush
数値ライブラリで始める安全なプログラミング
-
About me Taichi Kushiro / SWE Alp, Inc. boykush boykush315
自己紹介
-
Our Products Scalebase is a SaaS for recurring service providers.
We automate billings based on complex pricing models and recurring charge contracts. 継続型サービス事業者向けのSaaSです。複雑なプライシング や継続課金契約に基づく請求を自動化します。
-
Now, to the point… 本題に入ります。
-
… about the accuracy of numbers when calculating charges. 料金計算における数値の精度についてです。
-
Calculating charges is a universal concern in applications; accuracy of
the numeric type is critical. 料金計算はアプリケーションにおける普遍的な関心事であ り、数値型の精度の考慮が非常に重要です。
-
General approach Calculating charges are generally done using decimal floating
point types such as scala.math.BigDecimal. To avoid binary floating point errors. scala> val double: Double = 1.0 - 0.9 val double: Double = 0.09999999999999998 scala> val decimal: BigDecimal = BigDecimal(1.0) - BigDecimal(0.9) val decimal: BigDecimal = 0.1 料金計算では一般に BigDecimal 型のような10進浮動小数点 型が用いられます。
-
However, decimal floating point types also has its issues. しかし10進浮動小数点型にも課題は残ります。
-
The critical decimal floating point issue The most critical issue
is the error in repeating decimal. In some cases, the final integer billing price contains errors. scala> val step1 = BigDecimal(8) / BigDecimal(30) val step1: BigDecimal = 0.2666666666666666666666666666666667 scala> val step2 = step1 * 3000 val step2: BigDecimal = 800.0000000000000000000000000000001 scala> val rounded = step2.setScale(0, BigDecimal.RoundingMode.UP) val rounded: BigDecimal = 801 最も重大な課題は循環小数における誤差です。最終的な整数 の請求金額に誤差が生じるケースがあります。
-
Risk of scattered roundings Especially when calculating charges across multiple
contexts, it’s very difficult to round at many locations while keeping accuracy in mind. 特に複数の文脈に跨がって料金計算をする場合、精度を保ち ながら多くの箇所で端数処理を行うのはとても大変です。
-
How to solve these issues? 課題を解決するには?
-
The numerical library typelevel/spire is a solution 数値ライブラリ typelevel/spire によって解決します。
-
The Spire library provides numeric types to be generic, fast
and precise efficient syntax for writing numeric code Spireライブラリは汎用的で高速かつ高精度な数値と、効率 的な数値コードシンタックスを提供します。
-
Let’s look at an exact rational number type 正確な有理数型を紹介します。
-
The Rational type Represents rational numbers as a ratio between
two Longs. Convertible to BigDecimal type as an approximation. scala> import spire.math.Rational scala> val rational = Rational(1, 3) val rational: spire.math.Rational = 1/3 scala> val substracted = rational - Rational(1, 2) val substracted: spire.math.Rational = -1/6 scala> val appr = rational.toBigDecimal(4, java.math.RoundingMode.DOWN) val rounded: BigDecimal = 0.3333 Rational型は分数として2つの整数を保持し、有理数を表現 します。近似値としてBigDecimal型に変換可能です。
-
Exact repeating decimals The issue of repeating decimals is solved
with the Rational number type. scala> val step1 = Rational(8, 30) val step1: spire.math.Rational = 4/15 scala> val step2 = step1 * Rational(3000) val step2: spire.math.Rational = 800 scala> val rounded = step2.toBigDecimal(0, java.math.RoundingMode.UP) val rounded: BigDecimal = 800 循環小数の課題はRational型によって解決できます。
-
Localization of accuracy concerns When calculating charges across multiple contexts,
the concern for accuracy is eliminated except at the conversion to the final calculation result. 複数の文脈に跨って料金計算をする場合、精度への関心を最 終的な計算結果への変換に閉じることができます。
-
Safe programming with minimal testing!! テストを最小限に安全なプログラミングができる
-
Other else types? その他の型は?
-
The Real type Represents real numbers including irrational numbers. We
can apply to precision to obtain a Rational. scala> import spire.math.Real scala> val rational = Real(Rational(1, 3)) val rational: spire.math.Real = 1/3 scala> val irrational = Real(2).sqrt val irrational: spire.math.Real = 1.414213562373095048801688724209698078 scala> (rational + irrational).toRational(10) val res0: spire.math.Rational = 895/512 Real型は無理数を含む実数を表現します。精度を適用して有 理数を得ることができます。
-
Closest approximation to the desired accuracy Irrationals of the Real
type are implemented internally via functions from precision to approximation. Unlike Float and Double, the approximation is not stored for each operation. def +(y: Real): Real = (x, y) match { case (Exact(nx), Exact(ny)) => Exact(nx + ny) case (Exact(Rational.zero), _) => y case (_, Exact(Rational.zero)) => x case _ => Real(p => roundUp(Rational(x(p + 2) + y(p + 2), 4))) } Real型の無理数は精度を受け取り近似値を返す関数で実装さ れています。求める精度に最も近い近似値を得ます。
-
The Polynomial type Represents polynomial with a single variable (written.
x). We can substitute to the variable x to obtain a Rational. scala> import spire.math.Polynomial scala> val perUnit = Polynomial.linear(Rational(100)) val perUnit: spire.math.Polynomial[spire.math.Rational] = (100x) scala> val discount = Polynomial.constant(Rational(500)) val discount: spire.math.Polynomial[spire.math.Rational] = (500) scala> val pricing = perUnit - discount val pricing: spire.math.Polynomial[spire.math.Rational] = (100x - 500) scala> val price = pricing.apply(Rational(20)) val price: spire.math.Rational = 1500 Polynomial型は一変数多項式を表現します。変数xに代入し て有理数を得ることができます。
-
Others include Algebraic, Natural, Interval, Complex, etc. 他にはAlgebraic, Natural, Interval,
Complex型などがあ ります。
-
Serialization of the Rational type Rational型のシリアライズ
-
Serialization in Applications Delegate lifecycle storage to a data store
Inter-service communication in servers Inter-layer communication with clients, etc. アプリケーションではデータストア、サービス間通信、レイ ヤ間通信等でシリアライズが行われます。
-
Serialization for data storage When internally serializing the Rational (or
Polynomial) type in data storage, interconvertibility with string types is possible. scala> val rational = Rational(1, 2) val rational: spire.math.Rational = 1/2 scala> val serialized = rational.toString() val serialized: String = 1/2 scala> val deserialized = Rational(serialized) val deserialized: spire.math.Rational = 1/2 データストアでRational型を内部的にシリアライズをする場 合、文字列型との相互変換が可能です。
-
Serialization for internal communication For internal communication of the Rational
type between services or layers, there are several options. Communicate between supporting string conversions Prepare the following definitions in a interface define language syntax = "proto3"; message Rational{ long numerator = 1; long denominator = 2; } サービスまたはレイヤ間でRational型を内部的にシリアライ ズをするにはいくつかの選択肢があります。
-
Efficient syntax for writing numeric code 効率的な数値コード記述
-
Built-in type classes For example, the Rational type provides a
type class implementation of a Ring. The spire.algebra.Ring is an alias for cats algebra. scala> import spire.algebra.Ring scala> val items = List(Rational(500), Rational(1000), Rational(1500)) val items: List[spire.math.Rational] = List(500, 1000, 1500) scala> given cats.Monoid[Rational] = Ring[Rational].additive lazy val given_Monoid_Rational: cats.kernel.Monoid[spire.math.Rational] scala> val subtotals = cats.Foldable[List].fold(items) val subtotals: spire.math.Rational = 3000 Ring(環)のような型クラス実装が標準で提供されます。 spire.algebra.Ring は cats algebra のエイリアスです。
-
Conclusion Rational number types can easily be used when numerical
precision is required. The Spire library localizes the concern in accuracy as in lazy evaluation. You will be able to take advantage of convenient string conversions and syntax. 有理数型は簡単に正確な数値を扱えます。Spireライブラリ は精度の関心を遅延評価のように局所化します。
-
Safe programming life! 安全なプログラミングライフを!