fifth version

This commit is contained in:
2026-06-08 09:03:37 +00:00
parent b9efa98758
commit 2d57c4ff1f
26 changed files with 896 additions and 159 deletions
+6 -5
View File
@@ -9,8 +9,9 @@ import (
)
var (
ErrInvalidTick = errors.New("tick must be positive")
ErrInvalidBase = errors.New("base must be positive")
ErrInvalidTick = errors.New("tick must be positive")
ErrInvalidBase = errors.New("base must be positive")
ErrInvalidQuotation = errors.New("decimal cannot be represented as protobuf quotation")
)
type RoundMode int
@@ -28,7 +29,7 @@ func QuotationToDecimal(q *pb.Quotation) decimal.Decimal {
return decimal.NewFromInt(q.GetUnits()).Add(decimal.New(int64(q.GetNano()), -9))
}
func DecimalToQuotation(d decimal.Decimal) *pb.Quotation {
func DecimalToQuotation(d decimal.Decimal) (*pb.Quotation, error) {
units := d.Truncate(0)
nano := d.Sub(units).Mul(decimal.NewFromInt(1_000_000_000)).Round(0)
if nano.Equal(decimal.NewFromInt(1_000_000_000)) {
@@ -41,12 +42,12 @@ func DecimalToQuotation(d decimal.Decimal) *pb.Quotation {
}
nanoPart := nano.IntPart()
if nanoPart < -999_999_999 || nanoPart > 999_999_999 {
panic("decimal quotation nano is out of protobuf range")
return nil, ErrInvalidQuotation
}
return &pb.Quotation{
Units: units.IntPart(),
Nano: int32(nanoPart), // #nosec G115 -- nanoPart is bounded above.
}
}, nil
}
func MoneyValueToDecimal(v *pb.MoneyValue) decimal.Decimal {
+15
View File
@@ -37,3 +37,18 @@ func TestRoundToTick(t *testing.T) {
}
}
}
func TestDecimalToQuotationHandlesRoundingCarry(t *testing.T) {
tooPrecise := d("0.0000000005")
if _, err := DecimalToQuotation(tooPrecise); err != nil {
t.Fatalf("roundable quotation returned error: %v", err)
}
hugeNano := d("0.9999999996")
got, err := DecimalToQuotation(hugeNano)
if err != nil {
t.Fatalf("carry quotation returned error: %v", err)
}
if got.Units != 1 || got.Nano != 0 {
t.Fatalf("quotation=%+v, want carry to 1/0", got)
}
}