go datetme d datetime

This commit is contained in:
杨红岩 2022-10-05 15:33:17 +08:00
parent 6951cda31e
commit 7d009f8bad
15 changed files with 6359 additions and 29 deletions

View File

@ -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)开发的桌面客户端框架
### 授权 ### 授权

View File

@ -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
} }

View File

@ -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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

160
decimal/rounding.go Normal file
View 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
}
}
}

View File

@ -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++

View File

@ -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()

View File

@ -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事件注册")
}) })