From 3908de65f948aaf3662e566b1ea23e1618782c11 Mon Sep 17 00:00:00 2001 From: Jan Kadel Date: Sat, 24 May 2025 18:21:39 +0200 Subject: [PATCH] add floor and ceil methods --- fixed.go | 62 ++++++++++++++++++++++++++++++++++++++++ fixed_test.go | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/fixed.go b/fixed.go index 5a90421..5f9c301 100644 --- a/fixed.go +++ b/fixed.go @@ -273,6 +273,68 @@ func (f Fixed) Round(n int) Fixed { return Fixed{fp: fp} } +func (f Fixed) Floor(n int) Fixed { + if f.IsNaN() { + return NaN + } + + if n >= nPlaces { + // If n is greater or equal nPlaces, we would integer-divide + // fraction by zero, which is not possible and also not required + // as if f had fractions they are already represented up until the nPlaces-th decimal + // (in this case 8th decimal) + return f + } + + if n == 0 { + intpart := f.fp / scale + if f.fp < 0 && f.fp%scale != 0 { + intpart-- + } + return Fixed{fp: intpart * scale} + } + + fraction := f.fp % scale + f0 := fraction / int64(math.Pow10(nPlaces-n-1)) + if f.fp < 0 { + f0 += 1 * sign(f.fp) + } + f0 = (f0 / 10) + f0 = f0 * int64(math.Pow10(nPlaces-n)) + + intpart := f.fp - fraction + fp := intpart + f0 + + return Fixed{fp: fp} +} + +func (f Fixed) Ceil(n int) Fixed { + if f.IsNaN() { + return NaN + } + + if n >= nPlaces { + // If n is greater or equal nPlaces, we would integer-divide + // fraction by zero, which is not possible and also not required + // as if f had fractions they are already represented up until the nPlaces-th decimal + // (in this case 8th decimal) + return f + } + + fraction := f.fp % scale + f0 := fraction / int64(math.Pow10(nPlaces-n-1)) + f0 = (f0 / 10) + if f.fp > 0 { + f0 += 1 + } + f0 = f0 * int64(math.Pow10(nPlaces-n)) + + intpart := f.fp - fraction + fp := intpart + f0 + + return Fixed{fp: fp} +} + // Equal returns true if the f == f0. If either operand is NaN, false is returned. Use IsNaN() to test for NaN func (f Fixed) Equal(f0 Fixed) bool { if f.IsNaN() || f0.IsNaN() { diff --git a/fixed_test.go b/fixed_test.go index d6633a4..b9b6336 100644 --- a/fixed_test.go +++ b/fixed_test.go @@ -552,6 +552,84 @@ func TestRound(t *testing.T) { if f1.String() != "0.00000008" { t.Error("should be equal", f1, "0.00000008") } + + f0 = NewS("0.333") + f1 = f0.Round(8) + + if f1.String() != "0.333" { + t.Error("should be equal", f1, "0.333") + } + + f0 = NewS("22.333") + f1 = f0.Round(0) + + if f1.String() != "22" { + t.Error("should be equal", f1, "22") + } +} + +func TestFloor(t *testing.T) { + f0 := NewS("22.399") + f1 := f0.Floor(0) + require.Equal(t, "22", f1.String()) + f1 = f0.Floor(1) + require.Equal(t, "22.3", f1.String()) + f1 = f0.Floor(2) + require.Equal(t, "22.39", f1.String()) + + f0 = NewS("-0.79") + f1 = f0.Floor(0) + require.Equal(t, "-1", f1.String()) + + f0 = NewS("-123.79") + f1 = f0.Floor(0) + require.Equal(t, "-124", f1.String()) + + f0 = NewS("-0.00000008") + f1 = f0.Floor(8) + require.Equal(t, "-0.00000008", f1.String()) + + f0 = NewS("0.00000008") + f1 = f0.Floor(8) + require.Equal(t, "0.00000008", f1.String()) + + f0 = NewS("-0.0000008") + f1 = f0.Floor(8) + require.Equal(t, "-0.0000008", f1.String()) + + f0 = NewS("0.0000008") + f1 = f0.Floor(8) + require.Equal(t, "0.0000008", f1.String()) + + f0 = NewS("-0.0000008") + f1 = f0.Floor(6) + require.Equal(t, "0", f1.String()) + + f0 = NewS("0.0000008") + f1 = f0.Floor(6) + require.Equal(t, "0", f1.String()) +} + +func TestCeil(t *testing.T) { + f0 := NewS("22.399") + f1 := f0.Ceil(0) + require.Equal(t, "23", f1.String()) + f1 = f0.Ceil(1) + require.Equal(t, "22.4", f1.String()) + f1 = f0.Ceil(2) + require.Equal(t, "22.4", f1.String()) + + f0 = NewS("22.329") + f1 = f0.Ceil(2) + require.Equal(t, "22.33", f1.String()) + + f0 = NewS("-0.0000008") + f1 = f0.Ceil(6) + require.Equal(t, "0", f1.String()) + + f0 = NewS("0.0000008") + f1 = f0.Ceil(6) + require.Equal(t, "0.000001", f1.String()) } func TestEncodeDecode(t *testing.T) {