mirror of
https://gitee.com/energye/energy.git
synced 2024-12-02 03:37:48 +08:00
go datetme d datetime
This commit is contained in:
parent
6951cda31e
commit
7d009f8bad
@ -2,7 +2,9 @@
|
|||||||
[中文 ](https://github.com/energye/energy/blob/main/README.zh-CN.md)
|
[中文 ](https://github.com/energye/energy/blob/main/README.zh-CN.md)
|
||||||
[English](https://github.com/energye/energy/blob/main/README.md)
|
[English](https://github.com/energye/energy/blob/main/README.md)
|
||||||
|
|
||||||
golang 基于CEF的GUI框架
|
---
|
||||||
|
### 简介
|
||||||
|
#### [energy](https://github.com/energye/energy) 使用GO基于Chromium Embedded Framework (CEF)开发的桌面客户端框架
|
||||||
|
|
||||||
|
|
||||||
### 授权
|
### 授权
|
||||||
|
@ -145,7 +145,7 @@ func (m *browser) setOrIncNextWindowNum(browserId ...int32) int32 {
|
|||||||
} else {
|
} else {
|
||||||
m.windowSerial++
|
m.windowSerial++
|
||||||
}
|
}
|
||||||
logger.Logger.Debug("下一个窗口ID:", m.windowSerial)
|
logger.Logger.Debug("next window serial:", m.windowSerial)
|
||||||
return m.windowSerial
|
return m.windowSerial
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
. "github.com/energye/energy/consts"
|
. "github.com/energye/energy/consts"
|
||||||
|
"github.com/energye/energy/decimal"
|
||||||
"github.com/energye/golcl/dylib"
|
"github.com/energye/golcl/dylib"
|
||||||
"github.com/energye/golcl/lcl"
|
"github.com/energye/golcl/lcl"
|
||||||
"github.com/energye/golcl/lcl/api"
|
"github.com/energye/golcl/lcl/api"
|
||||||
@ -733,23 +734,27 @@ const (
|
|||||||
var dBaseDateTime = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
|
var dBaseDateTime = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
func GoDateTimeToDDateTime(dateTime time.Time) float64 {
|
func GoDateTimeToDDateTime(dateTime time.Time) float64 {
|
||||||
hour := dateTime.Hour()
|
|
||||||
minute := dateTime.Minute()
|
|
||||||
second := dateTime.Second()
|
|
||||||
date := float64(dateTime.Sub(dBaseDateTime).Milliseconds() / 1000 / 60 / 60 / 24)
|
date := float64(dateTime.Sub(dBaseDateTime).Milliseconds() / 1000 / 60 / 60 / 24)
|
||||||
dTime := (float64(hour)*dSecond + float64(minute)*float64(60) + float64(second)) / dDay
|
diHour := decimal.NewFromFloat(float64(dateTime.Hour()))
|
||||||
dTime, _ = strconv.ParseFloat(fmt.Sprintf("%.10f", date+dTime), 64)
|
diMinute := decimal.NewFromFloat(float64(dateTime.Minute())).Mul(decimal.NewFromFloat(60))
|
||||||
|
diSecond := decimal.NewFromFloat(float64(dateTime.Second()))
|
||||||
|
diTime := diHour.Mul(decimal.NewFromFloat(dSecond)).Add(diMinute).Add(diSecond).Div(decimal.NewFromFloat(dDay))
|
||||||
|
var dTime, _ = diTime.Add(decimal.NewFromFloat(date)).Float64()
|
||||||
return dTime
|
return dTime
|
||||||
}
|
}
|
||||||
|
|
||||||
func DDateTimeToGoDateTime(dateTime float64) time.Time {
|
func DDateTimeToGoDateTime(dateTime float64) time.Time {
|
||||||
dtStr := strings.Split(fmt.Sprintf("%v", dateTime), ".")
|
dtStr := strings.Split(fmt.Sprintf("%v", dateTime), ".")
|
||||||
dDate, _ := strconv.Atoi(dtStr[0])
|
dDate, _ := strconv.Atoi(dtStr[0])
|
||||||
dTime, _ := strconv.ParseFloat(fmt.Sprintf("%.10f", dateTime-float64(dDate)), 64)
|
diDateTime := decimal.NewFromFloat(dateTime)
|
||||||
gTime := time.Time{}
|
diDate := decimal.NewFromFloat(float64(dDate))
|
||||||
gTime = gTime.AddDate(1899, 12, 30)
|
diTime := diDateTime.Sub(diDate)
|
||||||
gTime = gTime.AddDate(-1, -1, dDate)
|
dTime, _ := diTime.Float64()
|
||||||
gTime = gTime.Add(time.Second * time.Duration(dTime*dDay))
|
gTime := time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
|
||||||
|
gTime = gTime.AddDate(0, 0, dDate)
|
||||||
|
diTime = decimal.NewFromFloat(float64(time.Second)).Mul(decimal.NewFromFloat(dTime).Mul(decimal.NewFromFloat(dDay)))
|
||||||
|
diTime = diTime.Add(decimal.NewFromFloat(dTime))
|
||||||
|
gTime = gTime.Add(time.Duration(diTime.IntPart()))
|
||||||
return gTime
|
return gTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
25
commons/commons_test.go
Normal file
25
commons/commons_test.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package commons
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGoTimeToDTime(t *testing.T) {
|
||||||
|
for i := 0; i < 1; i++ {
|
||||||
|
var now = time.Now()
|
||||||
|
fmt.Println("now", now)
|
||||||
|
var dNow = GoDateTimeToDDateTime(now)
|
||||||
|
fmt.Println("dNow", dNow)
|
||||||
|
var gNow = DDateTimeToGoDateTime(dNow)
|
||||||
|
fmt.Println("gNow", gNow)
|
||||||
|
fmt.Println("==========")
|
||||||
|
}
|
||||||
|
//1/24 + (1/24)/60*x
|
||||||
|
//x=60162037037
|
||||||
|
fmt.Println(63422453704 * 1000 / int64(time.Second))
|
||||||
|
fmt.Println(62710648148 * 1000 / int64(time.Second))
|
||||||
|
sub := time.Now().Sub(time.Date(2022, 10, 5, 0, 0, 0, 0, time.UTC))
|
||||||
|
fmt.Println(sub.Seconds())
|
||||||
|
}
|
49
decimal/CHANGELOG.md
Normal file
49
decimal/CHANGELOG.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
## Decimal v1.3.1
|
||||||
|
|
||||||
|
#### ENHANCEMENTS
|
||||||
|
- Reduce memory allocation in case of initialization from big.Int [#252](https://github.com/shopspring/decimal/pull/252)
|
||||||
|
|
||||||
|
#### BUGFIXES
|
||||||
|
- Fix binary marshalling of decimal zero value [#253](https://github.com/shopspring/decimal/pull/253)
|
||||||
|
|
||||||
|
## Decimal v1.3.0
|
||||||
|
|
||||||
|
#### FEATURES
|
||||||
|
- Add NewFromFormattedString initializer [#184](https://github.com/shopspring/decimal/pull/184)
|
||||||
|
- Add NewNullDecimal initializer [#234](https://github.com/shopspring/decimal/pull/234)
|
||||||
|
- Add implementation of natural exponent function (Taylor, Hull-Abraham) [#229](https://github.com/shopspring/decimal/pull/229)
|
||||||
|
- Add RoundUp, RoundDown, RoundCeil, RoundFloor methods [#196](https://github.com/shopspring/decimal/pull/196) [#202](https://github.com/shopspring/decimal/pull/202) [#220](https://github.com/shopspring/decimal/pull/220)
|
||||||
|
- Add XML support for NullDecimal [#192](https://github.com/shopspring/decimal/pull/192)
|
||||||
|
- Add IsInteger method [#179](https://github.com/shopspring/decimal/pull/179)
|
||||||
|
- Add Copy helper method [#123](https://github.com/shopspring/decimal/pull/123)
|
||||||
|
- Add InexactFloat64 helper method [#205](https://github.com/shopspring/decimal/pull/205)
|
||||||
|
- Add CoefficientInt64 helper method [#244](https://github.com/shopspring/decimal/pull/244)
|
||||||
|
|
||||||
|
#### ENHANCEMENTS
|
||||||
|
- Performance optimization of NewFromString init method [#198](https://github.com/shopspring/decimal/pull/198)
|
||||||
|
- Performance optimization of Abs and Round methods [#240](https://github.com/shopspring/decimal/pull/240)
|
||||||
|
- Additional tests (CI) for ppc64le architecture [#188](https://github.com/shopspring/decimal/pull/188)
|
||||||
|
|
||||||
|
#### BUGFIXES
|
||||||
|
- Fix rounding in FormatFloat fallback path (roundShortest method, fix taken from Go main repository) [#161](https://github.com/shopspring/decimal/pull/161)
|
||||||
|
- Add slice range checks to UnmarshalBinary method [#232](https://github.com/shopspring/decimal/pull/232)
|
||||||
|
|
||||||
|
## Decimal v1.2.0
|
||||||
|
|
||||||
|
#### BREAKING
|
||||||
|
- Drop support for Go version older than 1.7 [#172](https://github.com/shopspring/decimal/pull/172)
|
||||||
|
|
||||||
|
#### FEATURES
|
||||||
|
- Add NewFromInt and NewFromInt32 initializers [#72](https://github.com/shopspring/decimal/pull/72)
|
||||||
|
- Add support for Go modules [#157](https://github.com/shopspring/decimal/pull/157)
|
||||||
|
- Add BigInt, BigFloat helper methods [#171](https://github.com/shopspring/decimal/pull/171)
|
||||||
|
|
||||||
|
#### ENHANCEMENTS
|
||||||
|
- Memory usage optimization [#160](https://github.com/shopspring/decimal/pull/160)
|
||||||
|
- Updated travis CI golang versions [#156](https://github.com/shopspring/decimal/pull/156)
|
||||||
|
- Update documentation [#173](https://github.com/shopspring/decimal/pull/173)
|
||||||
|
- Improve code quality [#174](https://github.com/shopspring/decimal/pull/174)
|
||||||
|
|
||||||
|
#### BUGFIXES
|
||||||
|
- Revert remove insignificant digits [#159](https://github.com/shopspring/decimal/pull/159)
|
||||||
|
- Remove 15 interval for RoundCash [#166](https://github.com/shopspring/decimal/pull/166)
|
45
decimal/LICENSE
Normal file
45
decimal/LICENSE
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Spring, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
- Based on https://github.com/oguzbilgic/fpd, which has the following license:
|
||||||
|
"""
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Oguz Bilgic
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
"""
|
132
decimal/README.md
Normal file
132
decimal/README.md
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
# decimal
|
||||||
|
|
||||||
|
[![Github Actions](https://github.com/shopspring/decimal/actions/workflows/ci.yml/badge.svg)](https://github.com/shopspring/decimal/actions/workflows/ci.yml)
|
||||||
|
[![GoDoc](https://godoc.org/github.com/shopspring/decimal?status.svg)](https://godoc.org/github.com/shopspring/decimal)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/shopspring/decimal)](https://goreportcard.com/report/github.com/shopspring/decimal)
|
||||||
|
|
||||||
|
Arbitrary-precision fixed-point decimal numbers in go.
|
||||||
|
|
||||||
|
_Note:_ Decimal library can "only" represent numbers with a maximum of 2^31 digits after the decimal point.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* The zero-value is 0, and is safe to use without initialization
|
||||||
|
* Addition, subtraction, multiplication with no loss of precision
|
||||||
|
* Division with specified precision
|
||||||
|
* Database/sql serialization/deserialization
|
||||||
|
* JSON and XML serialization/deserialization
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Run `go get github.com/shopspring/decimal`
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
Decimal library requires Go version `>=1.7`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
price, err := decimal.NewFromString("136.02")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
quantity := decimal.NewFromInt(3)
|
||||||
|
|
||||||
|
fee, _ := decimal.NewFromString(".035")
|
||||||
|
taxRate, _ := decimal.NewFromString(".08875")
|
||||||
|
|
||||||
|
subtotal := price.Mul(quantity)
|
||||||
|
|
||||||
|
preTax := subtotal.Mul(fee.Add(decimal.NewFromFloat(1)))
|
||||||
|
|
||||||
|
total := preTax.Mul(taxRate.Add(decimal.NewFromFloat(1)))
|
||||||
|
|
||||||
|
fmt.Println("Subtotal:", subtotal) // Subtotal: 408.06
|
||||||
|
fmt.Println("Pre-tax:", preTax) // Pre-tax: 422.3421
|
||||||
|
fmt.Println("Taxes:", total.Sub(preTax)) // Taxes: 37.482861375
|
||||||
|
fmt.Println("Total:", total) // Total: 459.824961375
|
||||||
|
fmt.Println("Tax rate:", total.Sub(preTax).Div(preTax)) // Tax rate: 0.08875
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
http://godoc.org/github.com/shopspring/decimal
|
||||||
|
|
||||||
|
## Production Usage
|
||||||
|
|
||||||
|
* [Spring](https://shopspring.com/), since August 14, 2014.
|
||||||
|
* If you are using this in production, please let us know!
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
#### Why don't you just use float64?
|
||||||
|
|
||||||
|
Because float64 (or any binary floating point type, actually) can't represent
|
||||||
|
numbers such as `0.1` exactly.
|
||||||
|
|
||||||
|
Consider this code: http://play.golang.org/p/TQBd4yJe6B You might expect that
|
||||||
|
it prints out `10`, but it actually prints `9.999999999999831`. Over time,
|
||||||
|
these small errors can really add up!
|
||||||
|
|
||||||
|
#### Why don't you just use big.Rat?
|
||||||
|
|
||||||
|
big.Rat is fine for representing rational numbers, but Decimal is better for
|
||||||
|
representing money. Why? Here's a (contrived) example:
|
||||||
|
|
||||||
|
Let's say you use big.Rat, and you have two numbers, x and y, both
|
||||||
|
representing 1/3, and you have `z = 1 - x - y = 1/3`. If you print each one
|
||||||
|
out, the string output has to stop somewhere (let's say it stops at 3 decimal
|
||||||
|
digits, for simplicity), so you'll get 0.333, 0.333, and 0.333. But where did
|
||||||
|
the other 0.001 go?
|
||||||
|
|
||||||
|
Here's the above example as code: http://play.golang.org/p/lCZZs0w9KE
|
||||||
|
|
||||||
|
With Decimal, the strings being printed out represent the number exactly. So,
|
||||||
|
if you have `x = y = 1/3` (with precision 3), they will actually be equal to
|
||||||
|
0.333, and when you do `z = 1 - x - y`, `z` will be equal to .334. No money is
|
||||||
|
unaccounted for!
|
||||||
|
|
||||||
|
You still have to be careful. If you want to split a number `N` 3 ways, you
|
||||||
|
can't just send `N/3` to three different people. You have to pick one to send
|
||||||
|
`N - (2/3*N)` to. That person will receive the fraction of a penny remainder.
|
||||||
|
|
||||||
|
But, it is much easier to be careful with Decimal than with big.Rat.
|
||||||
|
|
||||||
|
#### Why isn't the API similar to big.Int's?
|
||||||
|
|
||||||
|
big.Int's API is built to reduce the number of memory allocations for maximal
|
||||||
|
performance. This makes sense for its use-case, but the trade-off is that the
|
||||||
|
API is awkward and easy to misuse.
|
||||||
|
|
||||||
|
For example, to add two big.Ints, you do: `z := new(big.Int).Add(x, y)`. A
|
||||||
|
developer unfamiliar with this API might try to do `z := a.Add(a, b)`. This
|
||||||
|
modifies `a` and sets `z` as an alias for `a`, which they might not expect. It
|
||||||
|
also modifies any other aliases to `a`.
|
||||||
|
|
||||||
|
Here's an example of the subtle bugs you can introduce with big.Int's API:
|
||||||
|
https://play.golang.org/p/x2R_78pa8r
|
||||||
|
|
||||||
|
In contrast, it's difficult to make such mistakes with decimal. Decimals
|
||||||
|
behave like other go numbers types: even though `a = b` will not deep copy
|
||||||
|
`b` into `a`, it is impossible to modify a Decimal, since all Decimal methods
|
||||||
|
return new Decimals and do not modify the originals. The downside is that
|
||||||
|
this causes extra allocations, so Decimal is less performant. My assumption
|
||||||
|
is that if you're using Decimals, you probably care more about correctness
|
||||||
|
than performance.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
This is a heavily modified fork of [fpd.Decimal](https://github.com/oguzbilgic/fpd), which was also released under the MIT License.
|
415
decimal/decimal-go.go
Normal file
415
decimal/decimal-go.go
Normal file
@ -0,0 +1,415 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Multiprecision decimal numbers.
|
||||||
|
// For floating-point formatting only; not general purpose.
|
||||||
|
// Only operations are assign and (binary) left/right shift.
|
||||||
|
// Can do binary floating point in multiprecision decimal precisely
|
||||||
|
// because 2 divides 10; cannot do decimal floating point
|
||||||
|
// in multiprecision binary precisely.
|
||||||
|
|
||||||
|
package decimal
|
||||||
|
|
||||||
|
type decimal struct {
|
||||||
|
d [800]byte // digits, big-endian representation
|
||||||
|
nd int // number of digits used
|
||||||
|
dp int // decimal point
|
||||||
|
neg bool // negative flag
|
||||||
|
trunc bool // discarded nonzero digits beyond d[:nd]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *decimal) String() string {
|
||||||
|
n := 10 + a.nd
|
||||||
|
if a.dp > 0 {
|
||||||
|
n += a.dp
|
||||||
|
}
|
||||||
|
if a.dp < 0 {
|
||||||
|
n += -a.dp
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, n)
|
||||||
|
w := 0
|
||||||
|
switch {
|
||||||
|
case a.nd == 0:
|
||||||
|
return "0"
|
||||||
|
|
||||||
|
case a.dp <= 0:
|
||||||
|
// zeros fill space between decimal point and digits
|
||||||
|
buf[w] = '0'
|
||||||
|
w++
|
||||||
|
buf[w] = '.'
|
||||||
|
w++
|
||||||
|
w += digitZero(buf[w : w+-a.dp])
|
||||||
|
w += copy(buf[w:], a.d[0:a.nd])
|
||||||
|
|
||||||
|
case a.dp < a.nd:
|
||||||
|
// decimal point in middle of digits
|
||||||
|
w += copy(buf[w:], a.d[0:a.dp])
|
||||||
|
buf[w] = '.'
|
||||||
|
w++
|
||||||
|
w += copy(buf[w:], a.d[a.dp:a.nd])
|
||||||
|
|
||||||
|
default:
|
||||||
|
// zeros fill space between digits and decimal point
|
||||||
|
w += copy(buf[w:], a.d[0:a.nd])
|
||||||
|
w += digitZero(buf[w : w+a.dp-a.nd])
|
||||||
|
}
|
||||||
|
return string(buf[0:w])
|
||||||
|
}
|
||||||
|
|
||||||
|
func digitZero(dst []byte) int {
|
||||||
|
for i := range dst {
|
||||||
|
dst[i] = '0'
|
||||||
|
}
|
||||||
|
return len(dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim trailing zeros from number.
|
||||||
|
// (They are meaningless; the decimal point is tracked
|
||||||
|
// independent of the number of digits.)
|
||||||
|
func trim(a *decimal) {
|
||||||
|
for a.nd > 0 && a.d[a.nd-1] == '0' {
|
||||||
|
a.nd--
|
||||||
|
}
|
||||||
|
if a.nd == 0 {
|
||||||
|
a.dp = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign v to a.
|
||||||
|
func (a *decimal) Assign(v uint64) {
|
||||||
|
var buf [24]byte
|
||||||
|
|
||||||
|
// Write reversed decimal in buf.
|
||||||
|
n := 0
|
||||||
|
for v > 0 {
|
||||||
|
v1 := v / 10
|
||||||
|
v -= 10 * v1
|
||||||
|
buf[n] = byte(v + '0')
|
||||||
|
n++
|
||||||
|
v = v1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse again to produce forward decimal in a.d.
|
||||||
|
a.nd = 0
|
||||||
|
for n--; n >= 0; n-- {
|
||||||
|
a.d[a.nd] = buf[n]
|
||||||
|
a.nd++
|
||||||
|
}
|
||||||
|
a.dp = a.nd
|
||||||
|
trim(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum shift that we can do in one pass without overflow.
|
||||||
|
// A uint has 32 or 64 bits, and we have to be able to accommodate 9<<k.
|
||||||
|
const uintSize = 32 << (^uint(0) >> 63)
|
||||||
|
const maxShift = uintSize - 4
|
||||||
|
|
||||||
|
// Binary shift right (/ 2) by k bits. k <= maxShift to avoid overflow.
|
||||||
|
func rightShift(a *decimal, k uint) {
|
||||||
|
r := 0 // read pointer
|
||||||
|
w := 0 // write pointer
|
||||||
|
|
||||||
|
// Pick up enough leading digits to cover first shift.
|
||||||
|
var n uint
|
||||||
|
for ; n>>k == 0; r++ {
|
||||||
|
if r >= a.nd {
|
||||||
|
if n == 0 {
|
||||||
|
// a == 0; shouldn't get here, but handle anyway.
|
||||||
|
a.nd = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for n>>k == 0 {
|
||||||
|
n = n * 10
|
||||||
|
r++
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c := uint(a.d[r])
|
||||||
|
n = n*10 + c - '0'
|
||||||
|
}
|
||||||
|
a.dp -= r - 1
|
||||||
|
|
||||||
|
var mask uint = (1 << k) - 1
|
||||||
|
|
||||||
|
// Pick up a digit, put down a digit.
|
||||||
|
for ; r < a.nd; r++ {
|
||||||
|
c := uint(a.d[r])
|
||||||
|
dig := n >> k
|
||||||
|
n &= mask
|
||||||
|
a.d[w] = byte(dig + '0')
|
||||||
|
w++
|
||||||
|
n = n*10 + c - '0'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put down extra digits.
|
||||||
|
for n > 0 {
|
||||||
|
dig := n >> k
|
||||||
|
n &= mask
|
||||||
|
if w < len(a.d) {
|
||||||
|
a.d[w] = byte(dig + '0')
|
||||||
|
w++
|
||||||
|
} else if dig > 0 {
|
||||||
|
a.trunc = true
|
||||||
|
}
|
||||||
|
n = n * 10
|
||||||
|
}
|
||||||
|
|
||||||
|
a.nd = w
|
||||||
|
trim(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cheat sheet for left shift: table indexed by shift count giving
|
||||||
|
// number of new digits that will be introduced by that shift.
|
||||||
|
//
|
||||||
|
// For example, leftcheats[4] = {2, "625"}. That means that
|
||||||
|
// if we are shifting by 4 (multiplying by 16), it will add 2 digits
|
||||||
|
// when the string prefix is "625" through "999", and one fewer digit
|
||||||
|
// if the string prefix is "000" through "624".
|
||||||
|
//
|
||||||
|
// Credit for this trick goes to Ken.
|
||||||
|
|
||||||
|
type leftCheat struct {
|
||||||
|
delta int // number of new digits
|
||||||
|
cutoff string // minus one digit if original < a.
|
||||||
|
}
|
||||||
|
|
||||||
|
var leftcheats = []leftCheat{
|
||||||
|
// Leading digits of 1/2^i = 5^i.
|
||||||
|
// 5^23 is not an exact 64-bit floating point number,
|
||||||
|
// so have to use bc for the math.
|
||||||
|
// Go up to 60 to be large enough for 32bit and 64bit platforms.
|
||||||
|
/*
|
||||||
|
seq 60 | sed 's/^/5^/' | bc |
|
||||||
|
awk 'BEGIN{ print "\t{ 0, \"\" }," }
|
||||||
|
{
|
||||||
|
log2 = log(2)/log(10)
|
||||||
|
printf("\t{ %d, \"%s\" },\t// * %d\n",
|
||||||
|
int(log2*NR+1), $0, 2**NR)
|
||||||
|
}'
|
||||||
|
*/
|
||||||
|
{0, ""},
|
||||||
|
{1, "5"}, // * 2
|
||||||
|
{1, "25"}, // * 4
|
||||||
|
{1, "125"}, // * 8
|
||||||
|
{2, "625"}, // * 16
|
||||||
|
{2, "3125"}, // * 32
|
||||||
|
{2, "15625"}, // * 64
|
||||||
|
{3, "78125"}, // * 128
|
||||||
|
{3, "390625"}, // * 256
|
||||||
|
{3, "1953125"}, // * 512
|
||||||
|
{4, "9765625"}, // * 1024
|
||||||
|
{4, "48828125"}, // * 2048
|
||||||
|
{4, "244140625"}, // * 4096
|
||||||
|
{4, "1220703125"}, // * 8192
|
||||||
|
{5, "6103515625"}, // * 16384
|
||||||
|
{5, "30517578125"}, // * 32768
|
||||||
|
{5, "152587890625"}, // * 65536
|
||||||
|
{6, "762939453125"}, // * 131072
|
||||||
|
{6, "3814697265625"}, // * 262144
|
||||||
|
{6, "19073486328125"}, // * 524288
|
||||||
|
{7, "95367431640625"}, // * 1048576
|
||||||
|
{7, "476837158203125"}, // * 2097152
|
||||||
|
{7, "2384185791015625"}, // * 4194304
|
||||||
|
{7, "11920928955078125"}, // * 8388608
|
||||||
|
{8, "59604644775390625"}, // * 16777216
|
||||||
|
{8, "298023223876953125"}, // * 33554432
|
||||||
|
{8, "1490116119384765625"}, // * 67108864
|
||||||
|
{9, "7450580596923828125"}, // * 134217728
|
||||||
|
{9, "37252902984619140625"}, // * 268435456
|
||||||
|
{9, "186264514923095703125"}, // * 536870912
|
||||||
|
{10, "931322574615478515625"}, // * 1073741824
|
||||||
|
{10, "4656612873077392578125"}, // * 2147483648
|
||||||
|
{10, "23283064365386962890625"}, // * 4294967296
|
||||||
|
{10, "116415321826934814453125"}, // * 8589934592
|
||||||
|
{11, "582076609134674072265625"}, // * 17179869184
|
||||||
|
{11, "2910383045673370361328125"}, // * 34359738368
|
||||||
|
{11, "14551915228366851806640625"}, // * 68719476736
|
||||||
|
{12, "72759576141834259033203125"}, // * 137438953472
|
||||||
|
{12, "363797880709171295166015625"}, // * 274877906944
|
||||||
|
{12, "1818989403545856475830078125"}, // * 549755813888
|
||||||
|
{13, "9094947017729282379150390625"}, // * 1099511627776
|
||||||
|
{13, "45474735088646411895751953125"}, // * 2199023255552
|
||||||
|
{13, "227373675443232059478759765625"}, // * 4398046511104
|
||||||
|
{13, "1136868377216160297393798828125"}, // * 8796093022208
|
||||||
|
{14, "5684341886080801486968994140625"}, // * 17592186044416
|
||||||
|
{14, "28421709430404007434844970703125"}, // * 35184372088832
|
||||||
|
{14, "142108547152020037174224853515625"}, // * 70368744177664
|
||||||
|
{15, "710542735760100185871124267578125"}, // * 140737488355328
|
||||||
|
{15, "3552713678800500929355621337890625"}, // * 281474976710656
|
||||||
|
{15, "17763568394002504646778106689453125"}, // * 562949953421312
|
||||||
|
{16, "88817841970012523233890533447265625"}, // * 1125899906842624
|
||||||
|
{16, "444089209850062616169452667236328125"}, // * 2251799813685248
|
||||||
|
{16, "2220446049250313080847263336181640625"}, // * 4503599627370496
|
||||||
|
{16, "11102230246251565404236316680908203125"}, // * 9007199254740992
|
||||||
|
{17, "55511151231257827021181583404541015625"}, // * 18014398509481984
|
||||||
|
{17, "277555756156289135105907917022705078125"}, // * 36028797018963968
|
||||||
|
{17, "1387778780781445675529539585113525390625"}, // * 72057594037927936
|
||||||
|
{18, "6938893903907228377647697925567626953125"}, // * 144115188075855872
|
||||||
|
{18, "34694469519536141888238489627838134765625"}, // * 288230376151711744
|
||||||
|
{18, "173472347597680709441192448139190673828125"}, // * 576460752303423488
|
||||||
|
{19, "867361737988403547205962240695953369140625"}, // * 1152921504606846976
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the leading prefix of b lexicographically less than s?
|
||||||
|
func prefixIsLessThan(b []byte, s string) bool {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if i >= len(b) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if b[i] != s[i] {
|
||||||
|
return b[i] < s[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary shift left (* 2) by k bits. k <= maxShift to avoid overflow.
|
||||||
|
func leftShift(a *decimal, k uint) {
|
||||||
|
delta := leftcheats[k].delta
|
||||||
|
if prefixIsLessThan(a.d[0:a.nd], leftcheats[k].cutoff) {
|
||||||
|
delta--
|
||||||
|
}
|
||||||
|
|
||||||
|
r := a.nd // read index
|
||||||
|
w := a.nd + delta // write index
|
||||||
|
|
||||||
|
// Pick up a digit, put down a digit.
|
||||||
|
var n uint
|
||||||
|
for r--; r >= 0; r-- {
|
||||||
|
n += (uint(a.d[r]) - '0') << k
|
||||||
|
quo := n / 10
|
||||||
|
rem := n - 10*quo
|
||||||
|
w--
|
||||||
|
if w < len(a.d) {
|
||||||
|
a.d[w] = byte(rem + '0')
|
||||||
|
} else if rem != 0 {
|
||||||
|
a.trunc = true
|
||||||
|
}
|
||||||
|
n = quo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put down extra digits.
|
||||||
|
for n > 0 {
|
||||||
|
quo := n / 10
|
||||||
|
rem := n - 10*quo
|
||||||
|
w--
|
||||||
|
if w < len(a.d) {
|
||||||
|
a.d[w] = byte(rem + '0')
|
||||||
|
} else if rem != 0 {
|
||||||
|
a.trunc = true
|
||||||
|
}
|
||||||
|
n = quo
|
||||||
|
}
|
||||||
|
|
||||||
|
a.nd += delta
|
||||||
|
if a.nd >= len(a.d) {
|
||||||
|
a.nd = len(a.d)
|
||||||
|
}
|
||||||
|
a.dp += delta
|
||||||
|
trim(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary shift left (k > 0) or right (k < 0).
|
||||||
|
func (a *decimal) Shift(k int) {
|
||||||
|
switch {
|
||||||
|
case a.nd == 0:
|
||||||
|
// nothing to do: a == 0
|
||||||
|
case k > 0:
|
||||||
|
for k > maxShift {
|
||||||
|
leftShift(a, maxShift)
|
||||||
|
k -= maxShift
|
||||||
|
}
|
||||||
|
leftShift(a, uint(k))
|
||||||
|
case k < 0:
|
||||||
|
for k < -maxShift {
|
||||||
|
rightShift(a, maxShift)
|
||||||
|
k += maxShift
|
||||||
|
}
|
||||||
|
rightShift(a, uint(-k))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we chop a at nd digits, should we round up?
|
||||||
|
func shouldRoundUp(a *decimal, nd int) bool {
|
||||||
|
if nd < 0 || nd >= a.nd {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even
|
||||||
|
// if we truncated, a little higher than what's recorded - always round up
|
||||||
|
if a.trunc {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return nd > 0 && (a.d[nd-1]-'0')%2 != 0
|
||||||
|
}
|
||||||
|
// not halfway - digit tells all
|
||||||
|
return a.d[nd] >= '5'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round a to nd digits (or fewer).
|
||||||
|
// If nd is zero, it means we're rounding
|
||||||
|
// just to the left of the digits, as in
|
||||||
|
// 0.09 -> 0.1.
|
||||||
|
func (a *decimal) Round(nd int) {
|
||||||
|
if nd < 0 || nd >= a.nd {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if shouldRoundUp(a, nd) {
|
||||||
|
a.RoundUp(nd)
|
||||||
|
} else {
|
||||||
|
a.RoundDown(nd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round a down to nd digits (or fewer).
|
||||||
|
func (a *decimal) RoundDown(nd int) {
|
||||||
|
if nd < 0 || nd >= a.nd {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.nd = nd
|
||||||
|
trim(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round a up to nd digits (or fewer).
|
||||||
|
func (a *decimal) RoundUp(nd int) {
|
||||||
|
if nd < 0 || nd >= a.nd {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// round up
|
||||||
|
for i := nd - 1; i >= 0; i-- {
|
||||||
|
c := a.d[i]
|
||||||
|
if c < '9' { // can stop after this digit
|
||||||
|
a.d[i]++
|
||||||
|
a.nd = i + 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number is all 9s.
|
||||||
|
// Change to single 1 with adjusted decimal point.
|
||||||
|
a.d[0] = '1'
|
||||||
|
a.nd = 1
|
||||||
|
a.dp++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract integer part, rounded appropriately.
|
||||||
|
// No guarantees about overflow.
|
||||||
|
func (a *decimal) RoundedInteger() uint64 {
|
||||||
|
if a.dp > 20 {
|
||||||
|
return 0xFFFFFFFFFFFFFFFF
|
||||||
|
}
|
||||||
|
var i int
|
||||||
|
n := uint64(0)
|
||||||
|
for i = 0; i < a.dp && i < a.nd; i++ {
|
||||||
|
n = n*10 + uint64(a.d[i]-'0')
|
||||||
|
}
|
||||||
|
for ; i < a.dp; i++ {
|
||||||
|
n *= 10
|
||||||
|
}
|
||||||
|
if shouldRoundUp(a, a.dp) {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
1904
decimal/decimal.go
Normal file
1904
decimal/decimal.go
Normal file
File diff suppressed because it is too large
Load Diff
250
decimal/decimal_bench_test.go
Normal file
250
decimal/decimal_bench_test.go
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
package decimal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DecimalSlice []Decimal
|
||||||
|
|
||||||
|
func (p DecimalSlice) Len() int { return len(p) }
|
||||||
|
func (p DecimalSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||||
|
func (p DecimalSlice) Less(i, j int) bool { return p[i].Cmp(p[j]) < 0 }
|
||||||
|
|
||||||
|
func BenchmarkNewFromFloatWithExponent(b *testing.B) {
|
||||||
|
rng := rand.New(rand.NewSource(0xdead1337))
|
||||||
|
in := make([]float64, b.N)
|
||||||
|
for i := range in {
|
||||||
|
in[i] = rng.NormFloat64() * 10e20
|
||||||
|
}
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
in := rng.NormFloat64() * 10e20
|
||||||
|
_ = NewFromFloatWithExponent(in, math.MinInt32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNewFromFloat(b *testing.B) {
|
||||||
|
rng := rand.New(rand.NewSource(0xdead1337))
|
||||||
|
in := make([]float64, b.N)
|
||||||
|
for i := range in {
|
||||||
|
in[i] = rng.NormFloat64() * 10e20
|
||||||
|
}
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = NewFromFloat(in[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNewFromStringFloat(b *testing.B) {
|
||||||
|
rng := rand.New(rand.NewSource(0xdead1337))
|
||||||
|
in := make([]float64, b.N)
|
||||||
|
for i := range in {
|
||||||
|
in[i] = rng.NormFloat64() * 10e20
|
||||||
|
}
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
in := strconv.FormatFloat(in[i], 'f', -1, 64)
|
||||||
|
_, _ = NewFromString(in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_FloorFast(b *testing.B) {
|
||||||
|
input := New(200, 2)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
input.Floor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_FloorRegular(b *testing.B) {
|
||||||
|
input := New(200, -2)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
input.Floor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_DivideOriginal(b *testing.B) {
|
||||||
|
tcs := createDivTestCases()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for _, tc := range tcs {
|
||||||
|
d := tc.d
|
||||||
|
if sign(tc.d2) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d2 := tc.d2
|
||||||
|
prec := tc.prec
|
||||||
|
a := d.DivOld(d2, int(prec))
|
||||||
|
if sign(a) > 2 {
|
||||||
|
panic("dummy panic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_DivideNew(b *testing.B) {
|
||||||
|
tcs := createDivTestCases()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for _, tc := range tcs {
|
||||||
|
d := tc.d
|
||||||
|
if sign(tc.d2) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d2 := tc.d2
|
||||||
|
prec := tc.prec
|
||||||
|
a := d.DivRound(d2, prec)
|
||||||
|
if sign(a) > 2 {
|
||||||
|
panic("dummy panic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecimal_RoundCash_Five(b *testing.B) {
|
||||||
|
const want = "3.50"
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
val := New(3478, -3)
|
||||||
|
if have := val.StringFixedCash(5); have != want {
|
||||||
|
b.Fatalf("\nHave: %q\nWant: %q", have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Cmp(b *testing.B) {
|
||||||
|
decimals := DecimalSlice([]Decimal{})
|
||||||
|
for i := 0; i < 1000000; i++ {
|
||||||
|
decimals = append(decimals, New(int64(i), 0))
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
sort.Sort(decimals)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_decimal_Decimal_Add_different_precision(b *testing.B) {
|
||||||
|
d1 := NewFromFloat(1000.123)
|
||||||
|
d2 := NewFromFloat(500).Mul(NewFromFloat(0.12))
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
d1.Add(d2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_decimal_Decimal_Sub_different_precision(b *testing.B) {
|
||||||
|
d1 := NewFromFloat(1000.123)
|
||||||
|
d2 := NewFromFloat(500).Mul(NewFromFloat(0.12))
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
d1.Sub(d2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_decimal_Decimal_Add_same_precision(b *testing.B) {
|
||||||
|
d1 := NewFromFloat(1000.123)
|
||||||
|
d2 := NewFromFloat(500.123)
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
d1.Add(d2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_decimal_Decimal_Sub_same_precision(b *testing.B) {
|
||||||
|
d1 := NewFromFloat(1000.123)
|
||||||
|
d2 := NewFromFloat(500.123)
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
d1.Add(d2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecimal_IsInteger(b *testing.B) {
|
||||||
|
d := RequireFromString("12.000")
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
d.IsInteger()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecimal_NewFromString(b *testing.B) {
|
||||||
|
count := 72
|
||||||
|
prices := make([]string, 0, count)
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
prices = append(prices, fmt.Sprintf("%d.%d", i*100, i))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for _, p := range prices {
|
||||||
|
d, err := NewFromString(p)
|
||||||
|
if err != nil {
|
||||||
|
b.Log(d)
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecimal_NewFromString_large_number(b *testing.B) {
|
||||||
|
count := 72
|
||||||
|
prices := make([]string, 0, count)
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
prices = append(prices, "9323372036854775807.9223372036854775807")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for _, p := range prices {
|
||||||
|
d, err := NewFromString(p)
|
||||||
|
if err != nil {
|
||||||
|
b.Log(d)
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecimal_ExpHullAbraham(b *testing.B) {
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
d := RequireFromString("30.412346346346")
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = d.ExpHullAbrham(10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecimal_ExpTaylor(b *testing.B) {
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
d := RequireFromString("30.412346346346")
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = d.ExpTaylor(10)
|
||||||
|
}
|
||||||
|
}
|
3341
decimal/decimal_test.go
Normal file
3341
decimal/decimal_test.go
Normal file
File diff suppressed because it is too large
Load Diff
160
decimal/rounding.go
Normal file
160
decimal/rounding.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Multiprecision decimal numbers.
|
||||||
|
// For floating-point formatting only; not general purpose.
|
||||||
|
// Only operations are assign and (binary) left/right shift.
|
||||||
|
// Can do binary floating point in multiprecision decimal precisely
|
||||||
|
// because 2 divides 10; cannot do decimal floating point
|
||||||
|
// in multiprecision binary precisely.
|
||||||
|
|
||||||
|
package decimal
|
||||||
|
|
||||||
|
type floatInfo struct {
|
||||||
|
mantbits uint
|
||||||
|
expbits uint
|
||||||
|
bias int
|
||||||
|
}
|
||||||
|
|
||||||
|
var float32info = floatInfo{23, 8, -127}
|
||||||
|
var float64info = floatInfo{52, 11, -1023}
|
||||||
|
|
||||||
|
// roundShortest rounds d (= mant * 2^exp) to the shortest number of digits
|
||||||
|
// that will let the original floating point value be precisely reconstructed.
|
||||||
|
func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
|
||||||
|
// If mantissa is zero, the number is zero; stop now.
|
||||||
|
if mant == 0 {
|
||||||
|
d.nd = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute upper and lower such that any decimal number
|
||||||
|
// between upper and lower (possibly inclusive)
|
||||||
|
// will round to the original floating point number.
|
||||||
|
|
||||||
|
// We may see at once that the number is already shortest.
|
||||||
|
//
|
||||||
|
// Suppose d is not denormal, so that 2^exp <= d < 10^dp.
|
||||||
|
// The closest shorter number is at least 10^(dp-nd) away.
|
||||||
|
// The lower/upper bounds computed below are at distance
|
||||||
|
// at most 2^(exp-mantbits).
|
||||||
|
//
|
||||||
|
// So the number is already shortest if 10^(dp-nd) > 2^(exp-mantbits),
|
||||||
|
// or equivalently log2(10)*(dp-nd) > exp-mantbits.
|
||||||
|
// It is true if 332/100*(dp-nd) >= exp-mantbits (log2(10) > 3.32).
|
||||||
|
minexp := flt.bias + 1 // minimum possible exponent
|
||||||
|
if exp > minexp && 332*(d.dp-d.nd) >= 100*(exp-int(flt.mantbits)) {
|
||||||
|
// The number is already shortest.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// d = mant << (exp - mantbits)
|
||||||
|
// Next highest floating point number is mant+1 << exp-mantbits.
|
||||||
|
// Our upper bound is halfway between, mant*2+1 << exp-mantbits-1.
|
||||||
|
upper := new(decimal)
|
||||||
|
upper.Assign(mant*2 + 1)
|
||||||
|
upper.Shift(exp - int(flt.mantbits) - 1)
|
||||||
|
|
||||||
|
// d = mant << (exp - mantbits)
|
||||||
|
// Next lowest floating point number is mant-1 << exp-mantbits,
|
||||||
|
// unless mant-1 drops the significant bit and exp is not the minimum exp,
|
||||||
|
// in which case the next lowest is mant*2-1 << exp-mantbits-1.
|
||||||
|
// Either way, call it mantlo << explo-mantbits.
|
||||||
|
// Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1.
|
||||||
|
var mantlo uint64
|
||||||
|
var explo int
|
||||||
|
if mant > 1<<flt.mantbits || exp == minexp {
|
||||||
|
mantlo = mant - 1
|
||||||
|
explo = exp
|
||||||
|
} else {
|
||||||
|
mantlo = mant*2 - 1
|
||||||
|
explo = exp - 1
|
||||||
|
}
|
||||||
|
lower := new(decimal)
|
||||||
|
lower.Assign(mantlo*2 + 1)
|
||||||
|
lower.Shift(explo - int(flt.mantbits) - 1)
|
||||||
|
|
||||||
|
// The upper and lower bounds are possible outputs only if
|
||||||
|
// the original mantissa is even, so that IEEE round-to-even
|
||||||
|
// would round to the original mantissa and not the neighbors.
|
||||||
|
inclusive := mant%2 == 0
|
||||||
|
|
||||||
|
// As we walk the digits we want to know whether rounding up would fall
|
||||||
|
// within the upper bound. This is tracked by upperdelta:
|
||||||
|
//
|
||||||
|
// If upperdelta == 0, the digits of d and upper are the same so far.
|
||||||
|
//
|
||||||
|
// If upperdelta == 1, we saw a difference of 1 between d and upper on a
|
||||||
|
// previous digit and subsequently only 9s for d and 0s for upper.
|
||||||
|
// (Thus rounding up may fall outside the bound, if it is exclusive.)
|
||||||
|
//
|
||||||
|
// If upperdelta == 2, then the difference is greater than 1
|
||||||
|
// and we know that rounding up falls within the bound.
|
||||||
|
var upperdelta uint8
|
||||||
|
|
||||||
|
// Now we can figure out the minimum number of digits required.
|
||||||
|
// Walk along until d has distinguished itself from upper and lower.
|
||||||
|
for ui := 0; ; ui++ {
|
||||||
|
// lower, d, and upper may have the decimal points at different
|
||||||
|
// places. In this case upper is the longest, so we iterate from
|
||||||
|
// ui==0 and start li and mi at (possibly) -1.
|
||||||
|
mi := ui - upper.dp + d.dp
|
||||||
|
if mi >= d.nd {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
li := ui - upper.dp + lower.dp
|
||||||
|
l := byte('0') // lower digit
|
||||||
|
if li >= 0 && li < lower.nd {
|
||||||
|
l = lower.d[li]
|
||||||
|
}
|
||||||
|
m := byte('0') // middle digit
|
||||||
|
if mi >= 0 {
|
||||||
|
m = d.d[mi]
|
||||||
|
}
|
||||||
|
u := byte('0') // upper digit
|
||||||
|
if ui < upper.nd {
|
||||||
|
u = upper.d[ui]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Okay to round down (truncate) if lower has a different digit
|
||||||
|
// or if lower is inclusive and is exactly the result of rounding
|
||||||
|
// down (i.e., and we have reached the final digit of lower).
|
||||||
|
okdown := l != m || inclusive && li+1 == lower.nd
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case upperdelta == 0 && m+1 < u:
|
||||||
|
// Example:
|
||||||
|
// m = 12345xxx
|
||||||
|
// u = 12347xxx
|
||||||
|
upperdelta = 2
|
||||||
|
case upperdelta == 0 && m != u:
|
||||||
|
// Example:
|
||||||
|
// m = 12345xxx
|
||||||
|
// u = 12346xxx
|
||||||
|
upperdelta = 1
|
||||||
|
case upperdelta == 1 && (m != '9' || u != '0'):
|
||||||
|
// Example:
|
||||||
|
// m = 1234598x
|
||||||
|
// u = 1234600x
|
||||||
|
upperdelta = 2
|
||||||
|
}
|
||||||
|
// Okay to round up if upper has a different digit and either upper
|
||||||
|
// is inclusive or upper is bigger than the result of rounding up.
|
||||||
|
okup := upperdelta > 0 && (inclusive || upperdelta > 1 || ui+1 < upper.nd)
|
||||||
|
|
||||||
|
// If it's okay to do either, then round to the nearest one.
|
||||||
|
// If it's okay to do only one, do it.
|
||||||
|
switch {
|
||||||
|
case okdown && okup:
|
||||||
|
d.Round(mi + 1)
|
||||||
|
return
|
||||||
|
case okdown:
|
||||||
|
d.RoundDown(mi + 1)
|
||||||
|
return
|
||||||
|
case okup:
|
||||||
|
d.RoundUp(mi + 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,14 +2,14 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/energye/energy/cef"
|
"github.com/energye/energy/ipc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cef.UseNetIPCChannel = false
|
ipc.UseNetIPCChannel = false
|
||||||
cef.IPC.CreateRenderIPC(1, 11)
|
ipc.IPC.CreateRenderIPC(1, 11)
|
||||||
var i = 0
|
var i = 0
|
||||||
cef.IPC.Render().On("on-client", func(context cef.IIPCContext) {
|
ipc.IPC.Render().On("on-client", func(context ipc.IIPCContext) {
|
||||||
var message = context.Arguments()
|
var message = context.Arguments()
|
||||||
var data = message.GetString(0)
|
var data = message.GetString(0)
|
||||||
//context.Free()
|
//context.Free()
|
||||||
@ -20,10 +20,10 @@ func main() {
|
|||||||
for j := 0; j < 1; j++ {
|
for j := 0; j < 1; j++ {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
args := cef.NewArgumentList()
|
args := ipc.NewArgumentList()
|
||||||
args.SetString(0, "数据:"+fmt.Sprintf("%d", i))
|
args.SetString(0, "数据:"+fmt.Sprintf("%d", i))
|
||||||
cef.IPC.Render().Emit("on-server", args)
|
ipc.IPC.Render().Emit("on-server", args)
|
||||||
cef.IPC.Render().EmitAndCallback("on-server", args, func(context cef.IIPCContext) {
|
ipc.IPC.Render().EmitAndCallback("on-server", args, func(context ipc.IIPCContext) {
|
||||||
fmt.Println("客户端接收:", string(context.Message().Data()))
|
fmt.Println("客户端接收:", string(context.Message().Data()))
|
||||||
//context.Free()
|
//context.Free()
|
||||||
i++
|
i++
|
||||||
|
@ -2,14 +2,14 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/energye/energy/cef"
|
"github.com/energye/energy/ipc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cef.UseNetIPCChannel = false
|
ipc.UseNetIPCChannel = false
|
||||||
cef.IPC.CreateRenderIPC(1, 10)
|
ipc.IPC.CreateRenderIPC(1, 10)
|
||||||
var i = 0
|
var i = 0
|
||||||
cef.IPC.Render().On("on-client", func(context cef.IIPCContext) {
|
ipc.IPC.Render().On("on-client", func(context ipc.IIPCContext) {
|
||||||
var message = context.Arguments()
|
var message = context.Arguments()
|
||||||
var data = message.GetString(0)
|
var data = message.GetString(0)
|
||||||
//context.Free()
|
//context.Free()
|
||||||
@ -20,9 +20,9 @@ func main() {
|
|||||||
for j := 0; j < 1; j++ {
|
for j := 0; j < 1; j++ {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
args := cef.NewArgumentList()
|
args := ipc.NewArgumentList()
|
||||||
args.SetString(0, "数据:"+fmt.Sprintf("%d", i))
|
args.SetString(0, "数据:"+fmt.Sprintf("%d", i))
|
||||||
cef.IPC.Render().Emit("on-server", args)
|
ipc.IPC.Render().Emit("on-server", args)
|
||||||
//cef.IPC.Render().EmitAndCallback("on-server", args, func(context cef.IIPCContext) {
|
//cef.IPC.Render().EmitAndCallback("on-server", args, func(context cef.IIPCContext) {
|
||||||
// fmt.Println("客户端接收:", string(context.Message().Data()))
|
// fmt.Println("客户端接收:", string(context.Message().Data()))
|
||||||
// //context.Free()
|
// //context.Free()
|
||||||
|
@ -3,6 +3,8 @@ package src
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/energye/energy/cef"
|
"github.com/energye/energy/cef"
|
||||||
|
"github.com/energye/energy/commons"
|
||||||
|
"github.com/energye/energy/ipc"
|
||||||
"github.com/energye/golcl/lcl"
|
"github.com/energye/golcl/lcl"
|
||||||
"github.com/energye/golcl/lcl/types"
|
"github.com/energye/golcl/lcl/types"
|
||||||
)
|
)
|
||||||
@ -15,15 +17,15 @@ func MainBrowserInit() {
|
|||||||
config.SetEnableDevTools(true)
|
config.SetEnableDevTools(true)
|
||||||
cef.BrowserWindow.Config.SetChromiumConfig(config)
|
cef.BrowserWindow.Config.SetChromiumConfig(config)
|
||||||
//默认加载的URL
|
//默认加载的URL
|
||||||
if cef.IsWindows() {
|
if commons.IsWindows() {
|
||||||
cef.BrowserWindow.Config.DefaultUrl = "E:\\SWT\\gopath\\src\\swt-lazarus\\demo17-dll-load\\demo-golang-dll-01-chromium\\demos\\demo-sub-process\\resources\\demo-misc.html"
|
cef.BrowserWindow.Config.DefaultUrl = "E:\\SWT\\gopath\\src\\swt-lazarus\\demo17-dll-load\\demo-golang-dll-01-chromium\\demos\\demo-sub-process\\resources\\demo-misc.html"
|
||||||
} else if cef.IsLinux() {
|
} else if commons.IsLinux() {
|
||||||
cef.BrowserWindow.Config.DefaultUrl = "/home/sxm/app/swt/gopath/src/github.com/energye/energy/demos/demo-sub-process/resources/demo-misc.html"
|
cef.BrowserWindow.Config.DefaultUrl = "/home/sxm/app/swt/gopath/src/github.com/energye/energy/demos/demo-sub-process/resources/demo-misc.html"
|
||||||
} else if cef.IsDarwin() {
|
} else if commons.IsDarwin() {
|
||||||
cef.BrowserWindow.Config.DefaultUrl = "/Users/zhangli/go/src/github.com/energye/energy/demos/demo-sub-process/resources/demo-misc.html"
|
cef.BrowserWindow.Config.DefaultUrl = "/Users/zhangli/go/src/github.com/energye/energy/demos/demo-sub-process/resources/demo-misc.html"
|
||||||
}
|
}
|
||||||
//主进程 IPC事件
|
//主进程 IPC事件
|
||||||
cef.IPC.Browser().SetOnEvent(func(event cef.IEventOn) {
|
ipc.IPC.Browser().SetOnEvent(func(event ipc.IEventOn) {
|
||||||
fmt.Println("主进程IPC事件注册")
|
fmt.Println("主进程IPC事件注册")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user