func TestRound(t *testing.T) { tests := []struct { val, roundOn float64 places int want float64 }{ {1234.56, .5, 1, 1234.6}, {123.445, .5, 2, 123.45}, } for _, test := range tests { have := util.Round(test.val, test.roundOn, test.places) assert.EqualValues(t, test.want, have, "%v", test) } }
// Swedish applies the Swedish rounding. You may set the usual options. // TODO: Consider text/currency package based on Valuta field func (m Money) Swedish(opts ...Option) Money { m.Option(opts...) const ( roundOn float64 = .5 places int = 0 ) switch m.Interval { case Interval005: // NL, SG, SA, CH, TR, CL, IE // 5 cent rounding return m.Setf(util.Round(m.Getf()*20, roundOn, places) / 20) // base 5 case Interval010: // New Zealand & Hong Kong // 10 cent rounding // In Sweden between 1985 and 1992, prices were rounded up for sales // ending in 5 öre. return m.Setf(util.Round(m.Getf()*10, roundOn, places) / 10) case Interval015: // 10 cent rounding, special case // Special case: In NZ, it is up to the business to decide if they // will round 5¢ intervals up or down. The majority of retailers follow // government advice and round it down. if m.m%5 == 0 { m.m = m.m - 1 } return m.Setf(util.Round(m.Getf()*10, roundOn, places) / 10) case Interval025: // round to quarter return m.Setf(util.Round(m.Getf()*4, roundOn, places) / 4) case Interval050: // 50 cent rounding // The system used in Sweden from 1992 to 2010, in Norway from 1993 to 2012, // and in Denmark since 1 October 2008 is the following: // Sales ending in 1–24 öre round down to 0 öre. // Sales ending in 25–49 öre round up to 50 öre. // Sales ending in 51–74 öre round down to 50 öre. // Sales ending in 75–99 öre round up to the next whole Krone/krona. return m.Setf(util.Round(m.Getf()*2, roundOn, places) / 2) case Interval100: // The system used in Sweden since 30 September 2010 and used in Norway since 1 May 2012. // Sales ending in 1–49 öre/øre round down to 0 öre/øre. // Sales ending in 50–99 öre/øre round up to the next whole krona/krone. return m.Setf(util.Round(m.Getf()*1, roundOn, places) / 1) // ;-) } return m }
// FmtFloat64 formats a float value, does internal maybe incorrect rounding. // Thread safe func (no *Number) FmtFloat64(w io.Writer, f float64) (int, error) { sign := 1 if f < 0 { sign = -sign } // Special cases: // NaN = "NaN" // +Inf = "+Infinity" // -Inf = "-Infinity" if math.IsNaN(f) { return w.Write(no.sym.Nan) } if f > floatMax64 { no.mu.Lock() defer no.mu.Unlock() no.clearBuf() wr := utf8.EncodeRune(no.buf, no.sym.Infinity) return w.Write(no.buf[:wr]) } if f < -floatMax64 { no.mu.Lock() defer no.mu.Unlock() no.clearBuf() wr := utf8.EncodeRune(no.buf, no.sym.MinusSign) wr += utf8.EncodeRune(no.buf[wr:], no.sym.Infinity) no.buf = no.buf[:numberBufferSize] return w.Write(no.buf[:wr]) } if isInt(f) { // check if float is integer value return no.FmtInt64(w, int64(f)) } usedFmt, err := no.GetFormat(sign < 0) if err != nil { if PkgLog.IsDebug() { PkgLog.Debug("i18n.Number.FmtFloat64.GetFormat", "err", err, "format", usedFmt.String()) } return 0, errgo.Mask(err) } // to test the next lines: http://play.golang.org/p/L0ykFv3G4B precPow10 := math.Pow10(usedFmt.precision) var modf float64 if f > 0 { modf = f + (5 / (precPow10 * 10)) } else { modf = f - (5 / (precPow10 * 10)) } intgr, fracf := math.Modf(modf) if fracf < 0 { fracf = -fracf } fracI := int64(util.Round(fracf*precPow10, 0, usedFmt.precision)) return no.FmtNumber(w, sign, int64(intgr), intLen(fracI), fracI) }
// Mul multiplies two Currency types. Both types must have the same precision. func (m Money) Mul(d Money) Money { // @todo c.m*d.m will overflow int64 r := util.Round(float64(m.m*d.m)/m.dpf, .5, 0) return m.Set(int64(r)) }