scala.math.BigDecimal:1.2と1.20は等しい

Saurabh

DoubleまたはStringをscala.math.BigDecimalに変換するときに、精度と末尾のゼロを維持する方法は?

ユースケース-JSONメッセージでは、属性はString型であり、値は「1.20」です。しかし、Scalaでこの属性を読み取り、BigDecimalに変換しているときに、精度が失われ、1.2に変換されます。

ScalaREPLスクリーンショット

Andriy Plokhotnyuk

@Saurabhなんて素敵な質問でしょう!ユースケースを共有することが重要です。

私の答えは、最も安全で効率的な方法でそれを解決できると思います...短い形式でそれは次のとおりです。

値を正確に解析するには、jsoniter-scala使用しBigDecimalます。

任意の数値型のJSON文字列との間のエンコード/デコードは、コーデックごとまたはクラスフィールドごとに定義できます。以下のコードを参照してください。

1)依存関係をあなたのbuild.sbt:に追加します

libraryDependencies ++= Seq(
  "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core"   % "2.0.1",
  "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.0.1" % Provided // required only in compile-time
)

2)データ構造を定義し、ルート構造のコーデックを導出し、応答本文を解析してシリアル化します。

import com.github.plokhotnyuk.jsoniter_scala.core._
import com.github.plokhotnyuk.jsoniter_scala.macros._

case class Response(
  amount: BigDecimal,
  @stringified price: BigDecimal)

implicit val codec: JsonValueCodec[Response] = JsonCodecMaker.make {
  CodecMakerConfig
    .withIsStringified(false) // switch it on to stringify all numeric and boolean values in this codec
    .withBigDecimalPrecision(34) // set a precision to round up to decimal128 format: java.math.MathContext.DECIMAL128.getPrecision
    .withBigDecimalScaleLimit(6178) // limit scale to fit the decimal128 format: BigDecimal("0." + "0" * 33 + "1e-6143", java.math.MathContext.DECIMAL128).scale + 1
    .withBigDecimalDigitsLimit(308) // limit a number of mantissa digits to be parsed before rounding with the specified precision
}

val response = readFromArray("""{"amount":1000,"price":"1.20"}""".getBytes("UTF-8"))
val json = writeToArray(Response(amount = BigDecimal(1000), price = BigDecimal("1.20")))

3)結果をコンソールに印刷し、確認します。

println(response)
println(new String(json, "UTF-8"))

Response(1000,1.20)
{"amount":1000,"price":"1.20"}   

提案されたアプローチが安全なのはなぜですか?

ええと... JSONの解析は地雷原です。特に、BigDecimalその後に正確なを取得する場合はなおさらですScalaのほとんどのJSONパーサーは、O(n^2)複雑な文字列表現用のJavaのコンストラクターn仮数の桁数)を使用してこれを行い、結果を安全なオプションに丸めませんMathContext(デフォルトでは、MathContext.DECIMAL128値はScalaのBigDecimalコンストラクターで使用されます。操作)。

信頼できない入力を受け入れるシステムに対して、低帯域幅のDoS / DoW攻撃の下で脆弱性が発生します。以下は、クラスパスで最も人気のあるScala用JSONパーサーの最新バージョンを使用してScalaREPLで再現する方法の簡単な例です。

...
Starting scala interpreter...
Welcome to Scala 2.12.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_222).
Type in expressions for evaluation. Or try :help.

scala> def timed[A](f: => A): A = { val t = System.currentTimeMillis; val r = f; println(s"Elapsed time (ms): ${System.currentTimeMillis - t}"); r } 
timed: [A](f: => A)A

scala> timed(io.circe.parser.decode[BigDecimal]("9" * 1000000))
Elapsed time (ms): 29192
res0: Either[io.circe.Error,BigDecimal] = Right(999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999...

scala> timed(io.circe.parser.decode[BigDecimal]("1e-100000000").right.get + 1)
Elapsed time (ms): 87185
res1: scala.math.BigDecimal = 1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000...

最新の1Gbitネットワークの場合、1M桁の数字で悪意のあるメッセージを10ミリ秒受信すると、シングルコアで29秒の100%CPU負荷が発生する可能性があります。256を超えるコアを、全帯域幅レートで効果的にDoS処理できます。最後の式は、後続の操作+または-操作がScala 2.12.8で使用された場合に、13バイトの数値のメッセージを使用してCPUコアを約1.5分間書き込む方法を示しています

そして、jsoniter-scalaは、Scala 2.11.x、2.12.x、および2.13.xのこれらすべてのケースを処理します。

なぜそれが最も効率的ですか?

以下は、128個の小さい(最大34桁の仮数)値と中程度(128桁の)の配列の解析中のさまざまなJVMでのScalaのJSONパーサーのスループット(1秒あたりの操作数が多いほど良い)結果のグラフです。仮数)BigDecimalそれに応じた

ここに画像の説明を入力してください

ここに画像の説明を入力してください

BigDecimaljsoniter-scalaでの解析ルーチン

  • BigDecimal36桁までの小さな数値にコンパクトな表現の値を使用します

  • 37〜284桁の中番号には、より効率的なホットループを使用します

  • O(n^1.5)285桁を超える値の複雑さを持つ再帰的アルゴリズムに切り替えます

さらに、jsoniter-scalaは、JSONをUTF-8バイトからデータ構造に直接解析してシリアル化し、ランタイムリフレクション、中間AST、文字列、ハッシュマップを使用せずに、最小限の割り当てとコピーで非常に高速に実行します。GeoJSON、Google Maps API、OpenRTB、TwitterAPIのさまざまなデータ型と実際のメッセージサンプルの115のベンチマークの結果をここで参照ください

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

「1Math.pow2」がscalaで機能しないのはなぜですか?

分類Dev

Math.Pow(10、Math.Abs(1))は10に等しいですか?

分類Dev

Math.sign([])= 0、Math.sign([20])= 1、Math.sign([20、30、40])= NaNなのはなぜですか?

分類Dev

ScalaとJava BigDecimal

分類Dev

Scala:なぜl1 ::: l2はl1。:: :( l2)と等しくないのですか?

分類Dev

Javaの結果は(int)Math.pow(2、x)と1 << xで異なります

分類Dev

Math.sin`1`が機能し、Math.sin'1 'が機能しない理由

分類Dev

doubleと同等のjava.math.BigDecimal.scale()

分類Dev

停止 rnd = Math.floor(Math.random() * 3) + 1;

分類Dev

new BigDecimal( "0.015")。compareTo(new BigDecimal(0.015))が-1を返すのはなぜですか?

分類Dev

Math.pow(64、1 / 3)は不正確な4を返します

分類Dev

CoffeeScriptでMath.floorとして// 1を使用しても安全ですか?

分類Dev

Math.min([1,2])がNaNを返すのはなぜですか?

分類Dev

Math.min([1,2])がNaNを返すのはなぜですか?

分類Dev

fast-mathを使用しているときに、GCCまたはClangが1つの命令の逆数を最適化しないのはなぜですか

分類Dev

(1から4).toSetと(1から4).to [scala.collection.immutable.Set]のScalaの違いは?

分類Dev

math.exp(2)とmath.e ** 2の違い

分類Dev

DateTime Mathは2行のコードで機能しますが、1行では機能しません

分類Dev

平坦化されていないScalaの集合ベクトルとは何ですか:(1から2).flatMap((1から3).toSet.subsets(_))?

分類Dev

3つのMath.random()値を生成して連結することは、1つのMath.random()値よりもランダムですか?

分類Dev

java.math.BigIntegerはjava.math.BigDecimalにキャストできません

分類Dev

小さなdoubleに1を加算すると、Math.Net.Numericalを使用して1が返されます。

分類Dev

(int)Math.Pow(10、0)は1ではなく0を返します

分類Dev

Math.pow(0、0)=== 1なのはなぜですか?

分類Dev

Math.round(0.49999999999999994)が1を返すのはなぜですか?

分類Dev

scala.Function1をラムダとして書く正しい方法は何ですか?

分類Dev

Java BigDecimalが1E + 1を返すのはなぜですか?

分類Dev

Math.random()*50 + Math.random()*20 の分布は、Math.random()*70 とどのように比較されますか?

分類Dev

1 ** math.nanと0j ** math.nanのサプライズ

Related 関連記事

  1. 1

    「1Math.pow2」がscalaで機能しないのはなぜですか?

  2. 2

    Math.Pow(10、Math.Abs(1))は10に等しいですか?

  3. 3

    Math.sign([])= 0、Math.sign([20])= 1、Math.sign([20、30、40])= NaNなのはなぜですか?

  4. 4

    ScalaとJava BigDecimal

  5. 5

    Scala:なぜl1 ::: l2はl1。:: :( l2)と等しくないのですか?

  6. 6

    Javaの結果は(int)Math.pow(2、x)と1 << xで異なります

  7. 7

    Math.sin`1`が機能し、Math.sin'1 'が機能しない理由

  8. 8

    doubleと同等のjava.math.BigDecimal.scale()

  9. 9

    停止 rnd = Math.floor(Math.random() * 3) + 1;

  10. 10

    new BigDecimal( "0.015")。compareTo(new BigDecimal(0.015))が-1を返すのはなぜですか?

  11. 11

    Math.pow(64、1 / 3)は不正確な4を返します

  12. 12

    CoffeeScriptでMath.floorとして// 1を使用しても安全ですか?

  13. 13

    Math.min([1,2])がNaNを返すのはなぜですか?

  14. 14

    Math.min([1,2])がNaNを返すのはなぜですか?

  15. 15

    fast-mathを使用しているときに、GCCまたはClangが1つの命令の逆数を最適化しないのはなぜですか

  16. 16

    (1から4).toSetと(1から4).to [scala.collection.immutable.Set]のScalaの違いは?

  17. 17

    math.exp(2)とmath.e ** 2の違い

  18. 18

    DateTime Mathは2行のコードで機能しますが、1行では機能しません

  19. 19

    平坦化されていないScalaの集合ベクトルとは何ですか:(1から2).flatMap((1から3).toSet.subsets(_))?

  20. 20

    3つのMath.random()値を生成して連結することは、1つのMath.random()値よりもランダムですか?

  21. 21

    java.math.BigIntegerはjava.math.BigDecimalにキャストできません

  22. 22

    小さなdoubleに1を加算すると、Math.Net.Numericalを使用して1が返されます。

  23. 23

    (int)Math.Pow(10、0)は1ではなく0を返します

  24. 24

    Math.pow(0、0)=== 1なのはなぜですか?

  25. 25

    Math.round(0.49999999999999994)が1を返すのはなぜですか?

  26. 26

    scala.Function1をラムダとして書く正しい方法は何ですか?

  27. 27

    Java BigDecimalが1E + 1を返すのはなぜですか?

  28. 28

    Math.random()*50 + Math.random()*20 の分布は、Math.random()*70 とどのように比較されますか?

  29. 29

    1 ** math.nanと0j ** math.nanのサプライズ

ホットタグ

アーカイブ