Arbitrary-precision fixed-point decimal numbers for Go, avoiding floating-point precision issues with support for arithmetic, rounding, serialization, and database integration.
NullDecimal is a nullable decimal type for use with database drivers that return NULL values. It wraps Decimal and adds a Valid boolean field following the sql.NullString / sql.NullFloat64 pattern.
type NullDecimal struct {
Decimal Decimal
Valid bool // Valid is true if the decimal is not NULL
}// NewNullDecimal creates a NullDecimal from a Decimal value.
// The returned NullDecimal has Valid=true.
func NewNullDecimal(d Decimal) NullDecimalUsage:
d := decimal.NewFromFloat(3.14)
nd := decimal.NewNullDecimal(d)
// nd.Decimal = 3.14, nd.Valid = true
// Zero value represents NULL
var nullND decimal.NullDecimal
// nullND.Valid = false// MarshalJSON implements json.Marshaler.
// Marshals to JSON null if Valid=false, otherwise marshals the Decimal value.
func (d NullDecimal) MarshalJSON() ([]byte, error)
// UnmarshalJSON implements json.Unmarshaler.
// Sets Valid=false for JSON null, Valid=true for valid decimal values.
func (d *NullDecimal) UnmarshalJSON(decimalBytes []byte) errorUsage:
import "encoding/json"
type Order struct {
Discount decimal.NullDecimal `json:"discount"`
}
// Marshal valid value
o := Order{Discount: decimal.NewNullDecimal(decimal.NewFromFloat(5.00))}
data, _ := json.Marshal(o)
// data: {"discount":"5"}
// Marshal null value
o2 := Order{} // Valid=false
data2, _ := json.Marshal(o2)
// data2: {"discount":null}
// Unmarshal
var o3 Order
json.Unmarshal([]byte(`{"discount":"10.5"}`), &o3)
// o3.Discount.Valid=true, o3.Discount.Decimal=10.5
var o4 Order
json.Unmarshal([]byte(`{"discount":null}`), &o4)
// o4.Discount.Valid=false// MarshalText implements encoding.TextMarshaler.
// Returns an empty byte slice if Valid=false.
func (d NullDecimal) MarshalText() (text []byte, err error)
// UnmarshalText implements encoding.TextUnmarshaler.
// Sets Valid=false for empty input, Valid=true otherwise.
func (d *NullDecimal) UnmarshalText(text []byte) error// Scan implements sql.Scanner for database deserialization.
// Sets Valid=false when the scanned value is nil (SQL NULL).
// Sets Valid=true and parses Decimal when value is non-nil.
func (d *NullDecimal) Scan(value interface{}) error
// Value implements driver.Valuer for database serialization.
// Returns nil driver.Value if Valid=false (writes SQL NULL).
// Returns string driver.Value if Valid=true.
func (d NullDecimal) Value() (driver.Value, error)Usage:
import (
"database/sql"
_ "github.com/lib/pq"
)
type Product struct {
ID int
Discount decimal.NullDecimal
}
db, _ := sql.Open("postgres", "...")
// Scan from query (handles NULL)
var p Product
row := db.QueryRow("SELECT id, discount FROM products WHERE id = $1", 1)
err := row.Scan(&p.ID, &p.Discount)
if p.Discount.Valid {
fmt.Println("Discount:", p.Discount.Decimal)
} else {
fmt.Println("No discount")
}
// Insert with possible NULL
var discount decimal.NullDecimal
// discount.Valid = false => inserts NULL
_, err = db.Exec("INSERT INTO products (discount) VALUES ($1)", discount)
// With value
discount = decimal.NewNullDecimal(decimal.NewFromFloat(10.0))
_, err = db.Exec("INSERT INTO products (discount) VALUES ($1)", discount)Install with Tessl CLI
npx tessl i tessl/golang-github-com-shopspring--decimal