Files
gocryptotrader/types/number.go
Gareth Kirwan f05f24da8b Types: Switch convert.StringToFloat64 to types.Number (#1415)
* Types: Add Number type

* Types: Switch StringToFloat64 for Number

This change mostly just renames the type.
convert package and StringToFloat64 represent actions, not types,
and make it misleading to use outside of the API context,
especially when using it for a Float64ToString operation.

* Common: Remove StringToFloat64

Replaced by types.Number

* fixup! Types: Switch StringToFloat64 for Number

Second pass at Okx

* Spellcheck: Fix whitespace handling for okx line
2023-12-20 13:01:27 +11:00

74 lines
2.0 KiB
Go

package types
import (
"errors"
"fmt"
"strconv"
"github.com/shopspring/decimal"
)
var errInvalidNumberValue = errors.New("invalid value for Number type")
// Number represents a floating point number, and implements json.Unmarshaller and json.Marshaller
type Number float64
// UnmarshalJSON implements json.Unmarshaler
func (f *Number) UnmarshalJSON(data []byte) error {
switch c := data[0]; c { // From json.decode literalInterface
case 'n', 't', 'f': // null, true, false
return fmt.Errorf("%w: %s", errInvalidNumberValue, data)
case '"': // string
if len(data) < 2 || data[len(data)-1] != '"' {
return fmt.Errorf("%w: %s", errInvalidNumberValue, data)
}
data = data[1 : len(data)-1] // Naive Unquote
default: // Should be a number
if c != '-' && (c < '0' || c > '9') { // Invalid json syntax
return fmt.Errorf("%w: %s", errInvalidNumberValue, data)
}
}
if len(data) == 0 {
*f = Number(0)
return nil
}
val, err := strconv.ParseFloat(string(data), 64)
if err != nil {
return fmt.Errorf("%w: %s", errInvalidNumberValue, data) // We don't use err; We know it's not valid and errInvalidNumberValue is clearer
}
*f = Number(val)
return nil
}
// MarshalJSON implements json.Marshaler by formatting to a json string
// 1337.37 will marshal to "1337.37"
// 0 will marshal to an empty string: ""
func (f Number) MarshalJSON() ([]byte, error) {
if f == 0 {
return []byte(`""`), nil
}
val := strconv.FormatFloat(float64(f), 'f', -1, 64)
return []byte(`"` + val + `"`), nil
}
// Float64 returns the underlying float64
func (f Number) Float64() float64 {
return float64(f)
}
// Int64 returns the truncated integer component of the number
func (f Number) Int64() int64 {
// It's likely this is sufficient, since Numbers probably have not had floating point math performed on them
// However if issues arise then we can switch to math.Round
return int64(f)
}
// Decimal returns a decimal.Decimal
func (f Number) Decimal() decimal.Decimal {
return decimal.NewFromFloat(float64(f))
}