Skip to content

Instantly share code, notes, and snippets.

@bcsmith2846
Last active June 7, 2022 13:29
Show Gist options
  • Save bcsmith2846/3f02a33a499cae9b48c7ae799f7ed909 to your computer and use it in GitHub Desktop.
Save bcsmith2846/3f02a33a499cae9b48c7ae799f7ed909 to your computer and use it in GitHub Desktop.
Gravel Decimal and Currency Types
package domain
import (
"fmt"
"github.com/bojanz/currency"
"github.com/cockroachdb/apd"
)
/*
Output:
[brenden@1337-lt gravel]$ gore -autoimport -pkg ./internal/domain
gore version 0.5.5 :help for help
added file internal/domain/address.go
added file internal/domain/billing_type.go
...
gore> TestNumbers()
Currency type w/o locale and formatter: 123.45 USD
Currency type: $123.45
Conditions: ; Errors: <nil>
(float64) .1 + .1 + .1 === .3: false
(apd.Decimal) ".1" + ".1" + ".1" == ".3": true
(apd.Decimal) ".1" + ".1" + ".1" == (3 * (10^-1)): true
(float64) sum (compiler cuts off zeros where precision is lost): 0.300000
(apd.Decimal) sum: 0.3
Percentages: 0.3 -> 30%
*/
/*
Notes:
- Arbitrary precision
- Implements sql.driver interface to be stored as a string
- Separates errors from "conditions" like undefined numbers,
InvalidOperations, etc.
- Has a type which will chain operations even after a failure
without a panic while still retaining the error condition
through the chain which can be displayed at the end
*/
type Decimal = *apd.Decimal
/*
Notes:
- Uses the apd.Decimal type in the currency.Amount struct
- Implements sql.driver interface to be stored as a composite type
containing two string parts: the value and the currency
*/
type Currency = currency.Amount
const (
CURRENCY = "USD"
LOCALE = "en-US"
)
func TestNumbers() {
amount, _ := currency.NewAmount("123.45", CURRENCY)
fmt.Printf("Currency type w/o locale and formatter: %s\n", amount.String())
locale := currency.NewLocale(LOCALE)
formatter := currency.NewFormatter(locale)
localizedString := formatter.Format(currency.Amount(amount))
fmt.Printf("Currency type: %s\n", localizedString)
viewInputOne := ".1"
var testNumOne float64
testNumOne = 0.1
firstNum, cond, err := apd.NewFromString(viewInputOne)
fmt.Printf("Conditions: %s; Errors: %v\n", cond, err)
viewInputTwo := ".1"
var testNumTwo float64
testNumTwo = 0.1
secondNum, _, _ := apd.NewFromString(viewInputTwo)
viewInputThree := ".1"
var testNumThree float64
testNumThree = 0.1
thirdNum, _, _ := apd.NewFromString(viewInputThree)
var testAgainst float64
testAgainst = 0.3
against, _, _ := apd.NewFromString(".3")
otherAgainst := apd.New(3, -1) // 3 * (10^-1)
var testSum float64
testSum = testNumOne + testNumTwo + testNumThree
sum := apd.New(0, 0) // 0 * (0 ^ 10)
ctx := apd.BaseContext.WithPrecision(32) // 32 digits of precision for this calculation context
ctx.Add(sum, firstNum, secondNum) // Has return values that specify errors as well as conditions
ctx.Add(sum, sum, thirdNum)
cmpTestOne := against.Cmp(sum)
cmpTestTwo := otherAgainst.Cmp(sum)
fmt.Printf("(float64) .1 + .1 + .1 === .3: %t\n", (testSum == testAgainst)) // false
fmt.Printf("(apd.Decimal) \".1\" + \".1\" + \".1\" == \".3\": %t\n", (cmpTestOne == 0)) // true
fmt.Printf("(apd.Decimal) \".1\" + \".1\" + \".1\" == (3 * (10^-1)): %t\n", (cmpTestTwo == 0)) // true
fmt.Printf("(float64) sum (compiler cuts off zeros where precision is lost): %f\n", testSum)
fmt.Printf("(apd.Decimal) sum: %s\n", sum.String())
percent := apd.New(1, 2) // could also be (100,0)
ctx.Mul(percent, percent, sum) // Has return values that specify errors as well as conditions
fmt.Printf("Percentages: %s -> %s%%\n", sum, percent.Text('f')) //Text('f') formats in non-exp form (not explicitly needed here)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment