Daml-LF: make MUL_NUMERIC and DIV_NUMERIC multi-scale (#2921)

* daml-lf: Make MUL_NUMERIC and DIV_NUMERIC multi-scale

* update release notes

* compiler: fix with type change
This commit is contained in:
Remy 2019-09-17 16:32:49 +02:00 committed by mergify[bot]
parent f32f1b975e
commit 220a03c9e8
14 changed files with 297 additions and 201 deletions

View File

@ -134,15 +134,17 @@ mkAnds [x] = x
mkAnds (x:xs) = mkAnd x $ mkAnds xs
alpha, beta :: TypeVarName
alpha, beta, gamma :: TypeVarName
-- NOTE(MH): We want to avoid shadowing variables in the environment. That's
-- what the weird names are for.
alpha = TypeVarName "::alpha::"
beta = TypeVarName "::beta::"
gamma = TypeVarName "::gamma::"
tAlpha, tBeta :: Type
tAlpha, tBeta, tGamma :: Type
tAlpha = TVar alpha
tBeta = TVar beta
tGamma = TVar gamma
infixr 1 :->

View File

@ -177,8 +177,8 @@ typeOfBuiltin = \case
BEGreaterEqNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TBool
BEAddNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TNumeric tAlpha
BESubNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TNumeric tAlpha
BEMulNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TNumeric tAlpha
BEDivNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TNumeric tAlpha
BEMulNumeric -> pure $ TForall (alpha, KNat) $ TForall (beta, KNat) $ TForall (gamma, KNat) $ TNumeric tAlpha :-> TNumeric tBeta :-> TNumeric tGamma
BEDivNumeric -> pure $ TForall (alpha, KNat) $ TForall (beta, KNat) $ TForall (gamma, KNat) $ TNumeric tAlpha :-> TNumeric tBeta :-> TNumeric tGamma
BERoundNumeric -> pure $ TForall (alpha, KNat) $ TInt64 :-> TNumeric tAlpha :-> TNumeric tAlpha
BEInt64ToNumeric -> pure $ TForall (alpha, KNat) $ TInt64 :-> TNumeric tAlpha
BENumericToInt64 -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TInt64

View File

@ -164,9 +164,9 @@ convertPrim _ "BEAddDecimal" (TNumeric10 :-> TNumeric10 :-> TNumeric10) =
convertPrim _ "BESubDecimal" (TNumeric10 :-> TNumeric10 :-> TNumeric10) =
ETyApp (EBuiltin BESubNumeric) (TNat 10)
convertPrim _ "BEMulDecimal" (TNumeric10 :-> TNumeric10 :-> TNumeric10) =
ETyApp (EBuiltin BEMulNumeric) (TNat 10)
ETyApp (ETyApp (ETyApp (EBuiltin BEMulNumeric) (TNat 10)) (TNat 10)) (TNat 10)
convertPrim _ "BEDivDecimal" (TNumeric10 :-> TNumeric10 :-> TNumeric10) =
ETyApp (EBuiltin BEDivNumeric) (TNat 10)
ETyApp (ETyApp (ETyApp (EBuiltin BEDivNumeric) (TNat 10)) (TNat 10)) (TNat 10)
convertPrim _ "BERoundDecimal" (TInt64 :-> TNumeric10 :-> TNumeric10) =
ETyApp (EBuiltin BERoundNumeric) (TNat 10)
convertPrim _ "BEEqual" (TNumeric10 :-> TNumeric10 :-> TBool) =

View File

@ -14,6 +14,7 @@ import com.digitalasset.daml.lf.language.{LanguageVersion => LV}
import com.digitalasset.daml_lf.{DamlLf1 => PLF}
import com.google.protobuf.CodedInputStream
import scala.annotation.tailrec
import scala.collection.JavaConverters._
import scala.collection.{breakOut, mutable}
@ -398,17 +399,11 @@ private[archive] class DecodeV1(minor: LV.Minor) extends Decode.OfPackage[PLF.Pa
case PLF.Expr.SumCase.BUILTIN =>
val info = DecodeV1.builtinInfoMap(lfExpr.getBuiltin)
assertSince(info.minVersion, lfExpr.getBuiltin.getValueDescriptor.getFullName)
if (info.handleLegacyDecimal) {
// FixMe: https://github.com/digital-asset/daml/issues/2289
// enable the check once the compiler produces proper DAML-LF 1.dev
// info.maxVersion.foreach(assertUntil(_, lfExpr.getBuiltin.getValueDescriptor.getFullName))
ETyApp(EBuiltin(info.builtin), TDecimalScale)
} else {
info.maxVersion.foreach(
assertUntil(_, lfExpr.getBuiltin.getValueDescriptor.getFullName))
EBuiltin(info.builtin)
}
info.maxVersion.foreach(assertUntil(_, lfExpr.getBuiltin.getValueDescriptor.getFullName))
ntimes[Expr](
info.implicitDecimalScaleParameters,
ETyApp(_, TDecimalScale),
EBuiltin(info.builtin))
case PLF.Expr.SumCase.REC_CON =>
val recCon = lfExpr.getRecCon
@ -791,6 +786,10 @@ private[archive] class DecodeV1(minor: LV.Minor) extends Decode.OfPackage[PLF.Pa
private[lf] object DecodeV1 {
@tailrec
private def ntimes[A](n: Int, f: A => A, a: A): A =
if (n == 0) a else ntimes(n - 1, f, f(a))
case class BuiltinTypeInfo(
proto: PLF.PrimType,
bTyp: BuiltinType,
@ -830,7 +829,7 @@ private[lf] object DecodeV1 {
builtin: BuiltinFunction,
minVersion: LV = LV.Features.default, // first version that does support the builtin
maxVersion: Option[LV] = None, // first version that does not support the builtin
handleLegacyDecimal: Boolean = false
implicitDecimalScaleParameters: Int = 0
)
val builtinFunctionInfos: List[BuiltinFunctionInfo] = {
@ -840,27 +839,32 @@ private[lf] object DecodeV1 {
ADD_DECIMAL,
BAddNumeric,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 1
),
BuiltinFunctionInfo(
SUB_DECIMAL,
BSubNumeric,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 1
),
BuiltinFunctionInfo(
MUL_DECIMAL,
BMulNumeric,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 3
),
BuiltinFunctionInfo(
DIV_DECIMAL,
BDivNumeric,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 3
),
BuiltinFunctionInfo(
ROUND_DECIMAL,
BRoundNumeric,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 1
),
BuiltinFunctionInfo(ADD_NUMERIC, BAddNumeric, minVersion = numeric),
BuiltinFunctionInfo(SUB_NUMERIC, BSubNumeric, minVersion = numeric),
BuiltinFunctionInfo(MUL_NUMERIC, BMulNumeric, minVersion = numeric),
@ -878,12 +882,14 @@ private[lf] object DecodeV1 {
INT64_TO_DECIMAL,
BInt64ToNumeric,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 1
),
BuiltinFunctionInfo(
DECIMAL_TO_INT64,
BNumericToInt64,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 1
),
BuiltinFunctionInfo(INT64_TO_NUMERIC, BInt64ToNumeric, minVersion = numeric),
BuiltinFunctionInfo(NUMERIC_TO_INT64, BNumericToInt64, minVersion = numeric),
BuiltinFunctionInfo(FOLDL, BFoldl),
@ -901,7 +907,8 @@ private[lf] object DecodeV1 {
LEQ_DECIMAL,
BLessEqNumeric,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 1
),
BuiltinFunctionInfo(LEQ_NUMERIC, BLessEqNumeric, minVersion = numeric),
BuiltinFunctionInfo(LEQ_TEXT, BLessEqText),
BuiltinFunctionInfo(LEQ_TIMESTAMP, BLessEqTimestamp),
@ -911,7 +918,8 @@ private[lf] object DecodeV1 {
GEQ_DECIMAL,
BGreaterEqNumeric,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 1
),
BuiltinFunctionInfo(GEQ_NUMERIC, BGreaterEqNumeric, minVersion = numeric),
BuiltinFunctionInfo(GEQ_TEXT, BGreaterEqText),
BuiltinFunctionInfo(GEQ_TIMESTAMP, BGreaterEqTimestamp),
@ -921,7 +929,8 @@ private[lf] object DecodeV1 {
LESS_DECIMAL,
BLessNumeric,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 1
),
BuiltinFunctionInfo(LESS_NUMERIC, BLessNumeric, minVersion = numeric),
BuiltinFunctionInfo(LESS_TEXT, BLessText),
BuiltinFunctionInfo(LESS_TIMESTAMP, BLessTimestamp),
@ -931,7 +940,8 @@ private[lf] object DecodeV1 {
GREATER_DECIMAL,
BGreaterNumeric,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 1
),
BuiltinFunctionInfo(GREATER_NUMERIC, BGreaterNumeric, minVersion = numeric),
BuiltinFunctionInfo(GREATER_TEXT, BGreaterText),
BuiltinFunctionInfo(GREATER_TIMESTAMP, BGreaterTimestamp),
@ -941,7 +951,8 @@ private[lf] object DecodeV1 {
TO_TEXT_DECIMAL,
BToTextNumeric,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 1
),
BuiltinFunctionInfo(TO_TEXT_NUMERIC, BToTextNumeric, minVersion = numeric),
BuiltinFunctionInfo(TO_TEXT_TIMESTAMP, BToTextTimestamp),
BuiltinFunctionInfo(TO_TEXT_PARTY, BToTextParty, minVersion = partyTextConversions),
@ -953,7 +964,7 @@ private[lf] object DecodeV1 {
BuiltinFunctionInfo(
FROM_TEXT_DECIMAL,
BFromTextNumeric,
handleLegacyDecimal = true,
implicitDecimalScaleParameters = 1,
minVersion = numberParsing,
maxVersion = Some(numeric)),
BuiltinFunctionInfo(FROM_TEXT_NUMERIC, BFromTextNumeric, minVersion = numeric),
@ -975,7 +986,8 @@ private[lf] object DecodeV1 {
EQUAL_DECIMAL,
BEqualNumeric,
maxVersion = Some(numeric),
handleLegacyDecimal = true),
implicitDecimalScaleParameters = 1
),
BuiltinFunctionInfo(EQUAL_NUMERIC, BEqualNumeric, minVersion = numeric),
BuiltinFunctionInfo(EQUAL_TEXT, BEqualText),
BuiltinFunctionInfo(EQUAL_TIMESTAMP, BEqualTimestamp),

View File

@ -7,11 +7,11 @@ import java.math.BigDecimal
import java.nio.file.{Files, Paths}
import com.digitalasset.daml.bazeltools.BazelRunfiles._
import com.digitalasset.daml.lf.archive.DecodeV1.BuiltinFunctionInfo
import com.digitalasset.daml.lf.archive.Reader.ParseError
import com.digitalasset.daml.lf.data.{ImmArray, Ref}
import com.digitalasset.daml.lf.language.Util._
import com.digitalasset.daml.lf.language.{Ast, LanguageVersion => LV}
import com.digitalasset.daml.lf.language.{Ast, LanguageMinorVersion, LanguageVersion => LV}
import LanguageMinorVersion.Implicits._
import com.digitalasset.daml_lf.DamlLf1
import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.{Inside, Matchers, OptionValues, WordSpec}
@ -217,8 +217,8 @@ class DecodeV1Spec
"decodeExpr" should {
def toProto(b: BuiltinFunctionInfo) =
DamlLf1.Expr.newBuilder().setBuiltin(b.proto).build()
def toProtoExpr(b: DamlLf1.BuiltinFunction) =
DamlLf1.Expr.newBuilder().setBuiltin(b).build()
def toDecimalProto(s: String) =
DamlLf1.Expr.newBuilder().setPrimLit(DamlLf1.PrimLit.newBuilder().setDecimal(s)).build()
@ -226,39 +226,102 @@ class DecodeV1Spec
def toNumericProto(s: String) =
DamlLf1.Expr.newBuilder().setPrimLit(DamlLf1.PrimLit.newBuilder().setNumeric(s)).build()
def toScala(b: BuiltinFunctionInfo) =
Ast.EBuiltin(b.builtin)
val decimalBuiltinTestCases = Table[DamlLf1.BuiltinFunction, LanguageMinorVersion, Ast.Expr](
("decimal builtins", "minVersion", "expected output"),
(
DamlLf1.BuiltinFunction.ADD_DECIMAL,
"1",
Ast.ETyApp(Ast.EBuiltin(Ast.BAddNumeric), TDecimalScale)),
(
DamlLf1.BuiltinFunction.SUB_DECIMAL,
"1",
Ast.ETyApp(Ast.EBuiltin(Ast.BSubNumeric), TDecimalScale)),
(
DamlLf1.BuiltinFunction.MUL_DECIMAL,
"1",
Ast.ETyApp(
Ast.ETyApp(Ast.ETyApp(Ast.EBuiltin(Ast.BMulNumeric), TDecimalScale), TDecimalScale),
TDecimalScale)),
(
DamlLf1.BuiltinFunction.DIV_DECIMAL,
"1",
Ast.ETyApp(
Ast.ETyApp(Ast.ETyApp(Ast.EBuiltin(Ast.BDivNumeric), TDecimalScale), TDecimalScale),
TDecimalScale)),
(
DamlLf1.BuiltinFunction.ROUND_DECIMAL,
"1",
Ast.ETyApp(Ast.EBuiltin(Ast.BRoundNumeric), TDecimalScale)),
(
DamlLf1.BuiltinFunction.LEQ_DECIMAL,
"1",
Ast.ETyApp(Ast.EBuiltin(Ast.BLessEqNumeric), TDecimalScale)),
(
DamlLf1.BuiltinFunction.LESS_DECIMAL,
"1",
Ast.ETyApp(Ast.EBuiltin(Ast.BLessNumeric), TDecimalScale)),
(
DamlLf1.BuiltinFunction.GEQ_DECIMAL,
"1",
Ast.ETyApp(Ast.EBuiltin(Ast.BGreaterEqNumeric), TDecimalScale)),
(
DamlLf1.BuiltinFunction.GREATER_DECIMAL,
"1",
Ast.ETyApp(Ast.EBuiltin(Ast.BGreaterNumeric), TDecimalScale)),
(
DamlLf1.BuiltinFunction.TO_TEXT_DECIMAL,
"1",
Ast.ETyApp(Ast.EBuiltin(Ast.BToTextNumeric), TDecimalScale)),
(
DamlLf1.BuiltinFunction.FROM_TEXT_DECIMAL,
"5",
Ast.ETyApp(Ast.EBuiltin(Ast.BFromTextNumeric), TDecimalScale)),
(
DamlLf1.BuiltinFunction.INT64_TO_DECIMAL,
"1",
Ast.ETyApp(Ast.EBuiltin(Ast.BInt64ToNumeric), TDecimalScale)),
(
DamlLf1.BuiltinFunction.DECIMAL_TO_INT64,
"1",
Ast.ETyApp(Ast.EBuiltin(Ast.BNumericToInt64), TDecimalScale)),
(
DamlLf1.BuiltinFunction.EQUAL_DECIMAL,
"1",
Ast.ETyApp(Ast.EBuiltin(Ast.BEqualNumeric), TDecimalScale)),
)
// All the legacy decimal bultins.
val decimalBuilttins =
DecodeV1.builtinFunctionInfos.filter(_.handleLegacyDecimal)
val numericBuiltinTestCases = Table(
"numeric builtins" -> "expected output",
DamlLf1.BuiltinFunction.ADD_NUMERIC -> Ast.EBuiltin(Ast.BAddNumeric),
DamlLf1.BuiltinFunction.SUB_NUMERIC -> Ast.EBuiltin(Ast.BSubNumeric),
DamlLf1.BuiltinFunction.MUL_NUMERIC -> Ast.EBuiltin(Ast.BMulNumeric),
DamlLf1.BuiltinFunction.DIV_NUMERIC -> Ast.EBuiltin(Ast.BDivNumeric),
DamlLf1.BuiltinFunction.ROUND_NUMERIC -> Ast.EBuiltin(Ast.BRoundNumeric),
DamlLf1.BuiltinFunction.LEQ_NUMERIC -> Ast.EBuiltin(Ast.BLessEqNumeric),
DamlLf1.BuiltinFunction.LESS_NUMERIC -> Ast.EBuiltin(Ast.BLessNumeric),
DamlLf1.BuiltinFunction.GEQ_NUMERIC -> Ast.EBuiltin(Ast.BGreaterEqNumeric),
DamlLf1.BuiltinFunction.GREATER_NUMERIC -> Ast.EBuiltin(Ast.BGreaterNumeric),
DamlLf1.BuiltinFunction.TO_TEXT_NUMERIC -> Ast.EBuiltin(Ast.BToTextNumeric),
DamlLf1.BuiltinFunction.FROM_TEXT_NUMERIC -> Ast.EBuiltin(Ast.BFromTextNumeric),
DamlLf1.BuiltinFunction.INT64_TO_NUMERIC -> Ast.EBuiltin(Ast.BInt64ToNumeric),
DamlLf1.BuiltinFunction.NUMERIC_TO_INT64 -> Ast.EBuiltin(Ast.BNumericToInt64),
DamlLf1.BuiltinFunction.EQUAL_NUMERIC -> Ast.EBuiltin(Ast.BEqualNumeric),
)
// All the numeric versions of the former.
val numericBuilttins = {
val isNumeric = decimalBuilttins.map(_.builtin).toSet
DecodeV1.builtinFunctionInfos.filter(info =>
!info.handleLegacyDecimal && isNumeric(info.builtin))
}
// Two other unrelated builtins, no need to test more.
val otherBuiltins =
DecodeV1.builtinFunctionInfos.filter(
info =>
info.proto == DamlLf1.BuiltinFunction.ADD_INT64 ||
info.proto == DamlLf1.BuiltinFunction.APPEND_TEXT)
assert(otherBuiltins.length == 2)
val decimalBuiltinTestCases = Table("decimal builtinInfo", numericBuilttins: _*)
val numericBuiltinTestCases = Table("numeric builtinInfo", numericBuilttins: _*)
val negativeBuiltinTestCases = Table("other builtinInfo", otherBuiltins: _*)
val negativeBuiltinTestCases = Table(
"other builtins" -> "expected output",
// We do not need to test all other builtin
DamlLf1.BuiltinFunction.ADD_INT64 -> Ast.EBuiltin(Ast.BAddInt64),
DamlLf1.BuiltinFunction.APPEND_TEXT -> Ast.EBuiltin(Ast.BAppendText)
)
"translate non numeric/decimal builtin as is for any version" in {
val allVersions = Table("all versions", preNumericMinVersions ++ postNumericMinVersions: _*)
forEvery(allVersions) { version =>
val decoder = moduleDecoder(version)
forEvery(negativeBuiltinTestCases) { info =>
decoder.decodeExpr(toProto(info), "test") shouldBe toScala(info)
forEvery(negativeBuiltinTestCases) { (proto, scala) =>
decoder.decodeExpr(toProtoExpr(proto), "test") shouldBe scala
}
}
}
@ -268,10 +331,9 @@ class DecodeV1Spec
forEvery(preNumericMinVersions) { version =>
val decoder = moduleDecoder(version)
forEvery(decimalBuiltinTestCases) { info =>
if (LV.ordering.gteq(LV(LV.Major.V1, version), info.minVersion))
decoder.decodeExpr(toProto(info), "test") shouldBe Ast
.ETyApp(toScala(info), Ast.TNat(10))
forEvery(decimalBuiltinTestCases) { (proto, minVersion, scala) =>
if (LV.Major.V1.minorVersionOrdering.gteq(version, minVersion))
decoder.decodeExpr(toProtoExpr(proto), "test") shouldBe scala
}
}
}
@ -281,20 +343,19 @@ class DecodeV1Spec
forEvery(preNumericMinVersions) { version =>
val decoder = moduleDecoder(version)
forEvery(numericBuiltinTestCases) { info =>
an[ParseError] shouldBe thrownBy(decoder.decodeExpr(toProto(info), "test"))
forEvery(numericBuiltinTestCases) { (proto, _) =>
an[ParseError] shouldBe thrownBy(decoder.decodeExpr(toProtoExpr(proto), "test"))
}
}
}
"translate Numeric builtins as is if version >= 1.dev" in {
forEvery(preNumericMinVersions) { version =>
forEvery(postNumericMinVersions) { version =>
val decoder = moduleDecoder(version)
forEvery(numericBuiltinTestCases) { info =>
if (LV.ordering.gteq(LV(LV.Major.V1, version), info.minVersion))
decoder.decodeExpr(toProto(info), "test") shouldBe toScala(info)
forEvery(numericBuiltinTestCases) { (proto, scala) =>
decoder.decodeExpr(toProtoExpr(proto), "test") shouldBe scala
}
}
}
@ -303,11 +364,11 @@ class DecodeV1Spec
// reactive the test once the decoder is not so lenient
"reject Decimal builtins if version >= 1.dev" ignore {
forEvery(preNumericMinVersions) { version =>
forEvery(postNumericMinVersions) { version =>
val decoder = moduleDecoder(version)
forEvery(decimalBuiltinTestCases) { info =>
an[ParseError] shouldBe thrownBy(decoder.decodeExpr(toProto(info), "test"))
forEvery(decimalBuiltinTestCases) { (proto, _, _) =>
an[ParseError] shouldBe thrownBy(decoder.decodeExpr(toProtoExpr(proto), "test"))
}
}
}

View File

@ -98,29 +98,24 @@ abstract class NumericModule {
}
/**
* Multiplies `x` by `y`. The output has the same scale as the inputs. If rounding must be
* Multiplies `x` by `y`. The output has the scale `scale`. If rounding must be
* performed, the [[https://en.wikipedia.org/wiki/Rounding#Round_half_to_even> banker's rounding convention]]
* is applied.
* In case of overflow, returns an error message instead.
*
* ```Requires the scale of `x` and `y` are the same.```
*/
final def multiply(x: Numeric, y: Numeric): Either[String, Numeric] = {
assert(x.scale == y.scale)
checkForOverflow((x multiply y).setScale(x.scale, ROUND_HALF_EVEN))
final def multiply(scale: Int, x: Numeric, y: Numeric): Either[String, Numeric] = {
checkForOverflow((x multiply y).setScale(scale, ROUND_HALF_EVEN))
}
/**
* Divides `x` by `y`. The output has the same scale as the inputs. If rounding must be
* Divides `x` by `y`. The output has the scale `scale`. If rounding must be
* performed, the [[https://en.wikipedia.org/wiki/Rounding#Round_half_to_even> banker's rounding convention]]
* is applied.
* In case of overflow, returns an error message instead.
*
* ```Requires the scale of `x` and `y` are the same.```
*/
final def divide(x: Numeric, y: Numeric): Either[String, Numeric] = {
assert(x.scale == y.scale)
checkForOverflow(x.divide(y, x.scale, ROUND_HALF_EVEN))
final def divide(scale: Int, x: Numeric, y: Numeric): Either[String, Numeric] = {
checkForOverflow(x.divide(y, scale, ROUND_HALF_EVEN))
}
/**

View File

@ -193,76 +193,85 @@ class NumericSpec
Numeric.assertFromString(s)
"return an error in case of overflow" in {
val testCases = Table[Numeric, Numeric](
("input1", "input2"),
("10000000000000000000.", "10000000000000000000."),
("10000000000000000000.0", "-1000000000000000000.0"),
("-1000000000000000000.00", "1000000000000000000.00"),
("-100000000000000000.000", "-1000000000000000000.000"),
("10.000000000000000000000000000000000000", "10.000000000000000000000000000000000000"),
("5678901234567890.12345678901234", "-5678901234567890.12345678901234"),
val testCases = Table[Int, Numeric, Numeric](
("scale", "input1", "input2"),
(0, "10000000000000000000.", "10000000000000000000."),
(1, "10000000000000000000.0", "-1000000000000000000.0"),
(2, "-1000000000000000000.00", "1000000000000000000.00"),
(3, "-100000000000000000.000", "-1000000000000000000.000"),
(36, "10.000000000000000000000000000000000000", "10.000000000000000000000000000000000000"),
(14, "5678901234567890.12345678901234", "-5678901234567890.12345678901234"),
)
multiply("10000000000000000000.", "1000000000000000000.") shouldBe 'right
multiply(0, "10000000000000000000.", "1000000000000000000.") shouldBe 'right
forEvery(testCases) { (x, y) =>
multiply(x, y) shouldBe 'left
forEvery(testCases) { (scale, x, y) =>
multiply(scale, x, y) shouldBe 'left
}
}
"multiply two numeric properly" in {
val testCases = Table[Numeric, Numeric, Numeric](
("input1", "input2", "result"),
("0.00000", "0.00000", "0.00000"),
val testCases = Table[Int, Numeric, Numeric, Numeric](
("scale", "input1", "input2", "result"),
(5, "0.00000", "0.00000", "0.00000"),
(
38,
"0.00000000000000000010000000000000000000",
"0.00000000000000000010000000000000000000",
"0.00000000000000000000000000000000000001"
),
(
37,
"1.0000000000000000000000000000000000000",
"-0.0000000000000000000000000000000000001",
"-0.0000000000000000000000000000000000001"
),
(
18,
"-1000000000000000000.000000000000000000",
"0.000000000000000001",
"-1.000000000000000000"
),
(
37,
"3.1415926535897932384626433832795028842",
"2.7182818284590452353602874713526624978",
"8.5397342226735670654635508695465744952"
),
(
1,
"0.5",
"0.1",
"0.0",
),
(
2,
"0.15",
"0.10",
"0.02",
),
(
3,
"1.006",
"0.100",
"0.101"
),
(
4,
"2.1003",
"0.1000",
"0.2100"
),
(
0,
"5555555555555555555.",
"4444444444444444444.",
"24691358024691358019753086419753086420."
)
)
forEvery(testCases) { (x, y, z) =>
multiply(x, y) shouldBe Right(z)
forEvery(testCases) { (scale, x, y, z) =>
multiply(scale, x, y) shouldBe Right(z)
}
}
@ -274,77 +283,82 @@ class NumericSpec
implicit def toNumeric(s: String): Numeric = Numeric.assertFromString(s)
"return an error in case of overflow" in {
val testCases = Table[Numeric, Numeric](
("input1", "input2"),
("1000000000000000000.0000000000", "0.0000000001"),
("-1000000000000000000000000000000000000.0", "0.1"),
val testCases = Table[Int, Numeric, Numeric](
("scale", "input1", "input2"),
(10, "1000000000000000000.0000000000", "0.0000000001"),
(1, "-1000000000000000000000000000000000000.0", "0.1"),
(
38,
"0.10000000000000000000000000000000000000",
"-0.10000000000000000000000000000000000000",
),
("5678901234567890.12345678901234", "-0.00000000001234"),
(14, "5678901234567890.12345678901234", "-0.00000000001234"),
)
divide("100000000000000000.0000000000", "0.0000000001") shouldBe 'right
divide(10, "100000000000000000.0000000000", "0.0000000001") shouldBe 'right
forEvery(testCases) { (x, y) =>
divide(x, y) shouldBe 'left
forEvery(testCases) { (scale, x, y) =>
divide(scale, x, y) shouldBe 'left
}
}
"divide two numerics properly" in {
val testCases = Table[Numeric, Numeric, Numeric](
("input1", "input2", "result"),
("0.00000", "1.00000", "0.00000"),
val testCases = Table[Int, Numeric, Numeric, Numeric](
("scale", "input1", "input2", "result"),
(5, "0.00000", "1.00000", "0.00000"),
(
38,
"0.00000000000000000010000000000000000000",
"-0.00000000000000000100000000000000000000",
"-0.10000000000000000000000000000000000000"
),
(
37,
"0.0000000000000000000000000000000000001",
"-0.1000000000000000000000000000000000001",
"-0.0000000000000000000000000000000000010"
),
(
18,
"1.000000000000000000",
"-0.000000000000000001",
"-1000000000000000000.000000000000000000",
),
(
37,
"3.1415926535897932384626433832795028842",
"2.7182818284590452353602874713526624978",
"1.1557273497909217179100931833126962991"
),
(
1,
"1.0",
"4.0",
"0.2",
),
(1, "6.0", "8.0", "0.8"),
(
"6.0",
"8.0",
"0.8",
),
(
3,
"1.006",
"10.000",
"0.101"
),
(
4,
"2.1003",
"10.0000",
"0.2100"
),
(
19,
"5555555555555555555.5555555555555555555",
"4343434343434343434.4343434343434343434",
"1.2790697674418604651"
)
)
forEvery(testCases) { (x, y, z) =>
divide(x, y) shouldBe Right(z)
forEvery(testCases) { (scale, x, y, z) =>
divide(scale, x, y) shouldBe Right(z)
}
}
}

View File

@ -157,8 +157,7 @@ private[digitalasset] class EncodeV1(val minor: LV.Minor) {
case TApp(fun, arg) => fun -> arg
})
private def ignoreFirstTNatForDecimalLegacy(typs: ImmArray[Type]): ImmArray[Type] =
// BTNumeric must be applied to a TNat that we should ignore
private def ignoreOneDecimalScaleParameter(typs: ImmArray[Type]): ImmArray[Type] =
typs match {
case ImmArrayCons(TNat(_), tail) => tail
case _ =>
@ -196,7 +195,7 @@ private[digitalasset] class EncodeV1(val minor: LV.Minor) {
case TBuiltin(bType) =>
val (proto, typs) =
if (bType == BTNumeric && versionIsOlderThan(LV.Features.numeric))
PLF.PrimType.DECIMAL -> ignoreFirstTNatForDecimalLegacy(args)
PLF.PrimType.DECIMAL -> ignoreOneDecimalScaleParameter(args)
else
builtinTypeInfoMap(bType).proto -> args
builder.setPrim(
@ -405,10 +404,10 @@ private[digitalasset] class EncodeV1(val minor: LV.Minor) {
case ETyAbs(binder, body) => binder -> body
})
private def isLegacyDecimalBuiltin(expr: Expr) =
private def implicitDecimalScaleParameters(expr: Expr) =
expr match {
case EBuiltin(f) => builtinFunctionMap(f).handleLegacyDecimal
case _ => false
case EBuiltin(f) => builtinFunctionMap(f).implicitDecimalScaleParameters
case _ => 0
}
private def encodeExprBuilder(expr0: Expr): PLF.Expr.Builder = {
@ -460,11 +459,8 @@ private[digitalasset] class EncodeV1(val minor: LV.Minor) {
case EApps(fun, args) =>
newBuilder.setApp(PLF.Expr.App.newBuilder().setFun(fun).accumulateLeft(args)(_ addArgs _))
case ETyApps(expr, typs1) =>
val typs: ImmArray[Type] =
if (isLegacyDecimalBuiltin(expr) && versionIsOlderThan(LV.Features.numeric))
ignoreFirstTNatForDecimalLegacy(typs1)
else
typs1
val typs =
ntimes(implicitDecimalScaleParameters(expr), ignoreOneDecimalScaleParameter, typs1)
newBuilder.setTyApp(
PLF.Expr.TyApp.newBuilder().setExpr(expr).accumulateLeft(typs)(_ addTypes _))
case ETyApps(expr, typs) =>
@ -602,6 +598,10 @@ private[digitalasset] class EncodeV1(val minor: LV.Minor) {
object EncodeV1 {
@tailrec
private def ntimes[A](n: Int, f: A => A, a: A): A =
if (n == 0) a else ntimes(n - 1, f, f(a))
private sealed abstract class LeftRecMatcher[Left, Right] {
def unapply(arg: Left): Option[(Left, ImmArray[Right])]
}

View File

@ -134,28 +134,28 @@ object SBuiltin {
Numeric.add(x, y)
)
private def divide(x: Numeric, y: Numeric): Numeric =
if (y.signum() == 0)
throw DamlEArithmeticError(
s"Attempt to divide ${Numeric.toString(x)} by ${Numeric.toString(y)}.")
else
rightOrArithmeticError(
s"(Numeric ${x.scale}) overflow when dividing ${Numeric.toString(x)} by ${Numeric.toString(y)}.",
Numeric.divide(x, y)
)
private def multiply(x: Numeric, y: Numeric): Numeric =
rightOrArithmeticError(
s"(Numeric ${x.scale}) overflow when multiplying ${Numeric.toString(x)} by ${Numeric.toString(y)}.",
Numeric.multiply(x, y)
)
private def subtract(x: Numeric, y: Numeric): Numeric =
rightOrArithmeticError(
s"(Numeric ${x.scale}) overflow when subtracting ${Numeric.toString(y)} from ${Numeric.toString(x)}.",
Numeric.subtract(x, y)
)
private def multiply(scale: Int, x: Numeric, y: Numeric): Numeric =
rightOrArithmeticError(
s"(Numeric ${scale}) overflow when multiplying ${Numeric.toString(x)} by ${Numeric.toString(y)}.",
Numeric.multiply(scale, x, y)
)
private def divide(scale: Int, x: Numeric, y: Numeric): Numeric =
if (y.signum() == 0)
throw DamlEArithmeticError(
s"Attempt to divide ${Numeric.toString(x)} by ${Numeric.toString(y)}.")
else
rightOrArithmeticError(
s"(Numeric ${scale}) overflow when dividing ${Numeric.toString(x)} by ${Numeric.toString(y)}.",
Numeric.divide(scale, x, y)
)
sealed abstract class SBBinaryOpNumeric(op: (Numeric, Numeric) => Numeric) extends SBuiltin(3) {
final def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
val scale = args.get(0).asInstanceOf[STNat].n
@ -166,10 +166,23 @@ object SBuiltin {
}
}
sealed abstract class SBBinaryOpNumeric2(op: (Int, Numeric, Numeric) => Numeric)
extends SBuiltin(5) {
final def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
val scaleA = args.get(0).asInstanceOf[STNat].n
val scaleB = args.get(1).asInstanceOf[STNat].n
val scale = args.get(2).asInstanceOf[STNat].n
val a = args.get(3).asInstanceOf[SNumeric].value
val b = args.get(4).asInstanceOf[SNumeric].value
assert(a.scale == scaleA && b.scale == scaleB)
machine.ctrl = CtrlValue(SNumeric(op(scale, a, b)))
}
}
final case object SBAddNumeric extends SBBinaryOpNumeric(add)
final case object SBSubNumeric extends SBBinaryOpNumeric(subtract)
final case object SBMulNumeric extends SBBinaryOpNumeric(multiply)
final case object SBDivNumeric extends SBBinaryOpNumeric(divide)
final case object SBMulNumeric extends SBBinaryOpNumeric2(multiply)
final case object SBDivNumeric extends SBBinaryOpNumeric2(divide)
final case object SBRoundNumeric extends SBuiltin(3) {
def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {

View File

@ -263,15 +263,16 @@ class SBuiltinTest extends FreeSpec with Matchers with TableDrivenPropertyChecks
val overSqrtOfTen = "3.1622776601683793319988935444327185338"
"throws exception in case of overflow" in {
eval(e"$builtin @0 1${"0" * 18}. 1${"0" * 19}.") shouldBe 'right
eval(e"$builtin @0 1${"0" * 19}. 1${"0" * 19}.") shouldBe 'left
eval(e"$builtin @37 $underSqrtOfTen $underSqrtOfTen") shouldBe 'right
eval(e"$builtin @37 $overSqrtOfTen $underSqrtOfTen") shouldBe 'left
eval(e"$builtin @10 1.1000000000 2.2000000000") shouldBe Right(SNumeric(n(10, 2.42)))
eval(e"$builtin @10 ${tenPowerOf(13)} ${tenPowerOf(14)}") shouldBe Right(
eval(e"$builtin @0 @0 @0 1${"0" * 18}. 1${"0" * 19}.") shouldBe 'right
eval(e"$builtin @0 @0 @0 1${"0" * 19}. 1${"0" * 19}.") shouldBe 'left
eval(e"$builtin @37 @37 @37 $underSqrtOfTen $underSqrtOfTen") shouldBe 'right
eval(e"$builtin @37 @37 @37 $overSqrtOfTen $underSqrtOfTen") shouldBe 'left
eval(e"$builtin @10 @10 @10 1.1000000000 2.2000000000") shouldBe Right(
SNumeric(n(10, 2.42)))
eval(e"$builtin @10 @10 @10 ${tenPowerOf(13)} ${tenPowerOf(14)}") shouldBe Right(
SNumeric(n(10, "1E27")))
eval(e"$builtin @10 ${tenPowerOf(14)} ${tenPowerOf(14)}") shouldBe 'left
eval(e"$builtin @10 ${s(10, bigBigDecimal)} ${bigBigDecimal - 1}") shouldBe Left(
eval(e"$builtin @10 @10 @10 ${tenPowerOf(14)} ${tenPowerOf(14)}") shouldBe 'left
eval(e"$builtin @10 @10 @10 ${s(10, bigBigDecimal)} ${bigBigDecimal - 1}") shouldBe Left(
DamlEArithmeticError(
s"(Numeric 10) overflow when multiplying ${s(10, bigBigDecimal)} by ${s(10, bigBigDecimal - 1)}.")
)
@ -281,25 +282,25 @@ class SBuiltinTest extends FreeSpec with Matchers with TableDrivenPropertyChecks
"DIV_NUMERIC" - {
val builtin = "DIV_NUMERIC"
"throws exception in case of overflow" in {
eval(e"$builtin @38 ${s(38, "1E-18")} ${s(38, "-1E-17")}") shouldBe 'right
eval(e"$builtin @38 ${s(38, "1E-18")} ${s(38, "-1E-18")}") shouldBe 'left
eval(e"$builtin @1 ${s(1, "1E36")} 0.2") shouldBe 'right
eval(e"$builtin @1 ${s(1, "1E36")} 0.1") shouldBe 'left
eval(e"$builtin @10 1.1000000000 2.2000000000") shouldBe Right(SNumeric(n(10, 0.5)))
eval(e"$builtin @10 ${s(10, bigBigDecimal)} ${tenPowerOf(-10)}") shouldBe 'left
eval(e"$builtin @10 ${tenPowerOf(17)} ${tenPowerOf(-10)}") shouldBe Right(
eval(e"$builtin @38 @38 @38 ${s(38, "1E-18")} ${s(38, "-1E-17")}") shouldBe 'right
eval(e"$builtin @38 @38 @38 ${s(38, "1E-18")} ${s(38, "-1E-18")}") shouldBe 'left
eval(e"$builtin @1 @1 @1 ${s(1, "1E36")} 0.2") shouldBe 'right
eval(e"$builtin @1 @1 @1 ${s(1, "1E36")} 0.1") shouldBe 'left
eval(e"$builtin @10 @10 @10 1.1000000000 2.2000000000") shouldBe Right(SNumeric(n(10, 0.5)))
eval(e"$builtin @10 @10 @10 ${s(10, bigBigDecimal)} ${tenPowerOf(-10)}") shouldBe 'left
eval(e"$builtin @10 @10 @10 ${tenPowerOf(17)} ${tenPowerOf(-10)}") shouldBe Right(
SNumeric(n(10, "1E27")))
eval(e"$builtin @10 ${tenPowerOf(18)} ${tenPowerOf(-10)}") shouldBe Left(
eval(e"$builtin @10 @10 @10 ${tenPowerOf(18)} ${tenPowerOf(-10)}") shouldBe Left(
DamlEArithmeticError(
s"(Numeric 10) overflow when dividing ${tenPowerOf(18)} by ${tenPowerOf(-10)}.")
)
}
"throws exception when divided by 0" in {
eval(e"$builtin @10 ${s(10, one)} ${tenPowerOf(-10)}") shouldBe Right(
eval(e"$builtin @10 @10 @10 ${s(10, one)} ${tenPowerOf(-10)}") shouldBe Right(
SNumeric(n(10, tenPowerOf(10))))
eval(e"$builtin @10 ${s(10, one)} ${s(10, zero)}") shouldBe 'left
eval(e"$builtin @10 ${s(10, bigBigDecimal)} ${s(10, zero)}") shouldBe Left(
eval(e"$builtin @10 @10 @10 ${s(10, one)} ${s(10, zero)}") shouldBe 'left
eval(e"$builtin @10 @10 @10 ${s(10, bigBigDecimal)} ${s(10, zero)}") shouldBe Left(
DamlEArithmeticError(s"Attempt to divide ${s(10, bigBigDecimal)} by 0.0000000000.")
)
@ -340,23 +341,23 @@ class SBuiltinTest extends FreeSpec with Matchers with TableDrivenPropertyChecks
val testCases = Table[String, (Numeric, Numeric) => Either[Any, SValue]](
("builtin", "reference"),
("ADD_NUMERIC", (a, b) => Right(SNumeric(n(10, a add b)))),
("SUB_NUMERIC", (a, b) => Right(SNumeric(n(10, a subtract b)))),
("MUL_NUMERIC", (a, b) => Right(SNumeric(round(a multiply b)))),
("ADD_NUMERIC @10", (a, b) => Right(SNumeric(n(10, a add b)))),
("SUB_NUMERIC @10", (a, b) => Right(SNumeric(n(10, a subtract b)))),
("MUL_NUMERIC @10 @10 @10 ", (a, b) => Right(SNumeric(round(a multiply b)))),
(
"DIV_NUMERIC",
"DIV_NUMERIC @10 @10 @10",
(a, b) => Either.cond(b.signum != 0, SNumeric(round(BigDecimal(a) / BigDecimal(b))), ())),
("LESS_EQ_NUMERIC", (a, b) => Right(SBool(BigDecimal(a) <= BigDecimal(b)))),
("GREATER_EQ_NUMERIC", (a, b) => Right(SBool(BigDecimal(a) >= BigDecimal(b)))),
("LESS_NUMERIC", (a, b) => Right(SBool(BigDecimal(a) < BigDecimal(b)))),
("GREATER_NUMERIC", (a, b) => Right(SBool(BigDecimal(a) > BigDecimal(b)))),
("EQUAL_NUMERIC", (a, b) => Right(SBool(BigDecimal(a) == BigDecimal(b)))),
("LESS_EQ_NUMERIC @10", (a, b) => Right(SBool(BigDecimal(a) <= BigDecimal(b)))),
("GREATER_EQ_NUMERIC @10", (a, b) => Right(SBool(BigDecimal(a) >= BigDecimal(b)))),
("LESS_NUMERIC @10", (a, b) => Right(SBool(BigDecimal(a) < BigDecimal(b)))),
("GREATER_NUMERIC @10", (a, b) => Right(SBool(BigDecimal(a) > BigDecimal(b)))),
("EQUAL_NUMERIC @10", (a, b) => Right(SBool(BigDecimal(a) == BigDecimal(b)))),
)
forEvery(testCases) { (builtin, ref) =>
forEvery(decimals) { a =>
forEvery(decimals) { b =>
eval(e"$builtin @10 ${s(10, a)} ${s(10, b)}").left
eval(e"$builtin ${s(10, a)} ${s(10, b)}").left
.map(_ => ()) shouldBe ref(n(10, a), n(10, b))
}
}

View File

@ -314,8 +314,8 @@ object Ast {
// Numeric arithmetic
final case object BAddNumeric extends BuiltinFunction(2) // : s. Numeric s Numeric s Numeric s
final case object BSubNumeric extends BuiltinFunction(2) // : s. Numeric s Numeric s Numeric s
final case object BMulNumeric extends BuiltinFunction(2) // : s. Numeric s Numeric s Numeric s
final case object BDivNumeric extends BuiltinFunction(2) // : s. Numeric s Numeric s Numeric s
final case object BMulNumeric extends BuiltinFunction(2) // : s1 s2 s. Numeric s1 Numeric s2 Numeric s
final case object BDivNumeric extends BuiltinFunction(2) // : s1 s2 s. Numeric s1 Numeric s2 Numeric s
final case object BRoundNumeric extends BuiltinFunction(2) // : s. Integer Numeric s Numeric s
final case object BCastNumeric extends BuiltinFunction(1) // : s1 s2. Numeric s1 Numeric s2
final case object BShiftNumeric extends BuiltinFunction(1) // : s1 s2. Numeric s1 Numeric s2

View File

@ -2156,33 +2156,23 @@ Numeric functions
scale of the inputs and the output is given by the type parameter
`α`. Throws an error if overflow.
* ``MUL_NUMERIC : ∀ (α : nat) . 'Numeric' α → 'Numeric' α → 'Numeric' α``
* ``MUL_NUMERIC : ∀ (α₁ α₂ α : nat) . 'Numeric' α → 'Numeric' α → 'Numeric' α``
Multiplies the two decimals and rounds the result to the closest
Multiplies the two numerics and rounds the result to the closest
multiple of ``10⁻ᵅ`` using `banker's rounding convention
<https://en.wikipedia.org/wiki/Rounding#Round_half_to_even>`_. The
scale of the inputs and the output is given by the type parameter
`α`. Throws an error in case of overflow.
<https://en.wikipedia.org/wiki/Rounding#Round_half_to_even>`_.
The type parameters `α₁`, `α₂`, `α` define the scale of the first
input, the second input, and the output, respectively. Throws an
error in case of overflow.
* ``DIV_NUMERIC : ∀ (α : nat) . 'Numeric' α → 'Numeric' α → 'Numeric' α``
* ``DIV_NUMERIC : ∀ (α₁ α₂ α : nat) . 'Numeric' α → 'Numeric' α → 'Numeric' α``
Divides the first decimal by the second one and rounds the result to
the closest multiple of ``10⁻ᵅ`` using `banker's rounding convention
<https://en.wikipedia.org/wiki/Rounding#Round_half_to_even>`_ (where
`n` is given as the type parameter). The scale of the inputs and
the output is given by the type parameter `α`. Throws an error in
case of overflow.
* ``ROUND_NUMERIC : ∀ (α : nat) . 'Int64' → 'Numeric' α → 'Numeric' α``
Rounds the decimal to the closest multiple of ``10ⁱ`` where ``i`` is
integer argument. In case the value to be rounded is exactly
half-way between two multiples, rounds toward the even one,
following the `banker's rounding convention
<https://en.wikipedia.org/wiki/Rounding#Round_half_to_even>`_. The
scale of the inputs and the output is given by the type parameter
`α`. Throws an exception if the integer is not between `α-37` and
`α` inclusive.
`n` is given as the type parameter). The type parameters `α₁`,
`α₂`, `α` define the scale of the first input, the second input, and
the output, respectively. Throws an error in case of overflow.
* ``CAST_NUMERIC : ∀ (α₁, α₂: nat) . 'Numeric' α₁ → 'Numeric' α₂``

View File

@ -42,8 +42,15 @@ private[validation] object Typing {
protected[validation] lazy val typeOfBuiltinFunction = {
val alpha = TVar(Name.assertFromString("$alpha$"))
val beta = TVar(Name.assertFromString("$beta$"))
val gamma = TVar(Name.assertFromString("$beta$"))
def tBinop(typ: Type): Type = typ ->: typ ->: typ
val tNumBinop = TForall(alpha.name -> KNat, tBinop(TNumeric(alpha)))
val tMultiNumBinop =
TForall(
alpha.name -> KNat,
TForall(
beta.name -> KNat,
TForall(gamma.name -> KNat, TNumeric(alpha) ->: TNumeric(beta) ->: TNumeric(gamma))))
val tNumConversion =
TForall(alpha.name -> KNat, TForall(beta.name -> KNat, TNumeric(alpha) ->: TNumeric(beta)))
def tComparison(bType: BuiltinType): Type = TBuiltin(bType) ->: TBuiltin(bType) ->: TBool
@ -54,8 +61,8 @@ private[validation] object Typing {
// Numeric arithmetic
BAddNumeric -> tNumBinop,
BSubNumeric -> tNumBinop,
BMulNumeric -> tNumBinop,
BDivNumeric -> tNumBinop,
BMulNumeric -> tMultiNumBinop,
BDivNumeric -> tMultiNumBinop,
BRoundNumeric -> TForall(alpha.name -> KNat, TInt64 ->: TNumeric(alpha) ->: TNumeric(alpha)),
BCastNumeric -> tNumConversion,
BShiftNumeric -> tNumConversion,

View File

@ -12,3 +12,4 @@ HEAD — ongoing
* [DAML Docs] suppress instance documentation when `--data-only` mode is requested
+ [DAML-LF] add CAST_NUMERIC and SHIFT_NUMERIC in DAML-LF 1.dev
+ [Sandbox] Dramatically increased performance of the ActiveContractService by only loading the contracts that the parties in the transaction filter are allowed to see.
+ [DAML-LF] change signature of MUL_NUMERIC and DIV_NUMERIC