Skip to content

Instantly share code, notes, and snippets.

@andrcmdr
Last active April 27, 2020 13:34
Show Gist options
  • Save andrcmdr/d0bb34aec060e8b88413487e7ba24e5a to your computer and use it in GitHub Desktop.
Save andrcmdr/d0bb34aec060e8b88413487e7ba24e5a to your computer and use it in GitHub Desktop.
Daily Clock Drift Correction (DCDC)
import (
math
os
strconv
)
fn dcdc(x f64) f64 {
return (x/(math.round(x/60)*60)-math.round(x/(math.round(x/60)*60)))*60*60*24*-1
}
fn main() {
println(dcdc(strconv.atof64(os.args[1])))
}
package main
import (
"fmt"
"os"
"strconv"
"math"
)
func dcdc(x float64) float64 {
return (x/(math.Round(x/60)*60)-math.Round(x/(math.Round(x/60)*60)))*60*60*24*-1
}
func main() {
if len(os.Args) > 1 {
x, e := strconv.ParseFloat(os.Args[1], 64)
if e != nil {
fmt.Println("Error in parcing of string to float!")
fmt.Println(e)
} else {
fmt.Println(dcdc(x))
}
} else {
fmt.Println("Type value in seconds as first input parameter/argument!")
}
}
package main
import (
"fmt"
"os"
// "strconv"
"math"
"math/big"
)
const (
rounding = big.ToNearestAway //big.ToNearestEven
prec = 1000
)
/*
func dcdc(x float64) float64 {
return (x/(math.Round(x/60)*60)-math.Round(x/(math.Round(x/60)*60)))*60*60*24*-1
}
*/
func dcdc(x *big.Float) *big.Float {
inv := new(big.Float).SetMode(rounding).SetPrec(prec).SetFloat64(-1)
daysec := new(big.Float).SetMode(rounding).SetPrec(prec).SetFloat64(60*60*24)
seconds := new(big.Float).SetMode(rounding).SetPrec(prec).SetFloat64(60)
minutes, _ := new(big.Float).SetMode(rounding).SetPrec(prec).Quo(x, seconds).Float64()
eq1 := new(big.Float).SetMode(rounding).SetPrec(prec).Quo(x, new(big.Float).SetMode(rounding).SetPrec(prec).Mul(new(big.Float).SetMode(rounding).SetPrec(prec).SetFloat64(math.Round(minutes)), seconds))
eq2, _ := new(big.Float).SetMode(rounding).SetPrec(prec).Quo(x, new(big.Float).SetMode(rounding).SetPrec(prec).Mul(new(big.Float).SetMode(rounding).SetPrec(prec).SetFloat64(math.Round(minutes)), seconds)).Float64()
eq3 := new(big.Float).SetMode(rounding).SetPrec(prec).Sub(eq1, new(big.Float).SetMode(rounding).SetPrec(prec).SetFloat64(math.Round(eq2)))
result := new(big.Float).SetMode(rounding).SetPrec(prec).Mul(new(big.Float).SetMode(rounding).SetPrec(prec).Mul(eq3, daysec), inv)
return result.SetMode(rounding).SetPrec(100)
}
func main() {
if len(os.Args) > 1 {
// x, e := strconv.ParseFloat(os.Args[1], 64)
x, _, e := big.ParseFloat(os.Args[1], 10, 1000, rounding)
if e != nil {
fmt.Println("Error in parcing of string to float!")
fmt.Println(e)
} else {
fmt.Println(dcdc(x))
}
} else {
fmt.Println("Type value in seconds as first input parameter/argument!")
}
}
package main
import (
"fmt"
"os"
// "strconv"
"math"
"math/big"
)
const (
rounding = big.ToNearestAway //big.ToNearestEven
prec = 1000
)
/*
func dcdc(x float64) float64 {
return (x/(math.Round(x/60)*60)-math.Round(x/(math.Round(x/60)*60)))*60*60*24*-1
}
*/
type bigFloat struct {
val *big.Float
}
// type bigFloat big.Float
type typef64 struct {
val float64
}
// type f64 float64
func (x *bigFloat) substract(y *bigFloat) *bigFloat {
return &bigFloat{new(big.Float).SetMode(rounding).SetPrec(prec).Sub(x.val, y.val)}
}
func (x *bigFloat) multiply(y *bigFloat) *bigFloat {
return &bigFloat{new(big.Float).SetMode(rounding).SetPrec(prec).Mul(x.val, y.val)}
}
func (x *bigFloat) divide(y *bigFloat) *bigFloat {
return &bigFloat{new(big.Float).SetMode(rounding).SetPrec(prec).Quo(x.val, y.val)}
}
func bigf64(x float64) *bigFloat {
return &bigFloat{new(big.Float).SetMode(rounding).SetPrec(prec).SetFloat64(x)}
}
func (x typef64) tobigf64() *bigFloat {
return &bigFloat{new(big.Float).SetMode(rounding).SetPrec(prec).SetFloat64(x.val)}
}
func f64(x *bigFloat) float64 {
x2f64, _ := x.val.Float64()
return x2f64
}
func (x *bigFloat) tof64() float64 {
x2f64, _ := x.val.Float64()
return x2f64
}
func round(x *bigFloat) *bigFloat {
x2f64, _ := x.val.Float64()
return &bigFloat{new(big.Float).SetMode(rounding).SetPrec(prec).SetFloat64(math.Round(x2f64))}
}
func (x *bigFloat) round() *bigFloat {
x2f64, _ := x.val.Float64()
return &bigFloat{new(big.Float).SetMode(rounding).SetPrec(prec).SetFloat64(math.Round(x2f64))}
}
func dcdc(x *bigFloat) *big.Float {
return x.divide(x.divide(bigf64(60)).round().multiply(bigf64(60))).substract(round(x.divide(x.divide(bigf64(60)).round().multiply(bigf64(60))))).multiply(bigf64(60*60*24*-1)).val.SetMode(rounding).SetPrec(100)
}
func main() {
if len(os.Args) > 1 {
// x, e := strconv.ParseFloat(os.Args[1], 64)
f, _, e := big.ParseFloat(os.Args[1], 10, 1000, rounding)
x := &bigFloat{f}
if e != nil {
fmt.Println("Error in parcing of string to float!")
fmt.Println(e)
} else {
fmt.Println(dcdc(x))
}
} else {
fmt.Println("Type value in seconds as first input parameter/argument!")
}
}
@andrcmdr
Copy link
Author

andrcmdr commented Apr 27, 2020

dcdc_v1.go

Первый набросок - довольно простая основная формула для вычисления корректровки при отклонении частоты и суточном дрифте системных часов на серверах для демона системного времени (в качестве входящего параметра корректировки) совместимого с протоколами PTP (для подключения GPS оборудования Symmetricom и Meinberg) и NTP

Компиляция:
go install -buildmode=shared std
go build -linkshared dcdc_v1.go

Нестандартный подход к компиляции в Go - динамически слинкованные скомпилированные файлы более компактны, но это порождает зависимость бинарного исполняемого файла от конкретной версии библиотеки libc используемой в сборке

dcdc_v2.go

C пакетом math/big для больших чисел и вычислений с произвольной точностью - выглядит ужасно и требует декомпозиции и рефакторинга

dcdc.v

Как то же самое выглядит на языке V при этом с той же высокой точностью вычислений (100 бит мантиссы)

Размер динамически слинкованных (по умолчанию) скомпилированных исполняемых файлов, порождаемых компилятором языка V (используется Clang, т.к. V фактически сейчас это Си transpiler) также весьма компактен (равноценно Go с динамической линковкой), т.к. в Clang по умолчанию используется динамическая линковка с системными библиотеками для компилируемых исполняемых бинарных файлов

dcdc_v3.go

Цепочка вычислений после рефакторинга через создание оберточных функций, их композиции через создание родительского типа над big.Float (через встраивание big.Float в структуру родительского типа)

На самом деле хорошая иллюстрация и опыт работы в Go с типами (также возможно с абстрактными типами, интерфейсами) и их расширением (способами создания extension методов для типов в Go), как для встроенных типов в stdlib так и для сторонних пакетов и модулей

Выводы

Иллюстрация работы и операций с большими числами (arbitrary precision arithmetic, арифметика произвольной точности) в языке Go где нет перегрузки операторов и нет привычных механизмов расширения встроенных и сторонних типов (типов в stdlib или в сторонних пакетах, например с помощью extension методов - встраивание типов в структуры не в счёт, это не позволяет без boxing/unboxing'а (встраивания и взятия/разыменовывания указателя на структуру/тип/интерфейс при возврате данных их функций) использовать те же типы в возврате методов/функций и совершать вызовы методов по цепочке, например для композиции функций в цепочки вычислений для math/big, композиции последовательностей действий, например для для ORM отображения структурированных данных, для создания DSL языков, создания иерархических API библиотек и фреймворков, майнинга данных и парсинга структур данных и т.д.

Данные ограничения языка по дизайну понятны - нельзя перегружать встраиваемые типы и операторы вычислений, чтобы новые зависимости включенные в проект не меняли семантики встроенных типов данных и оперторов (в Ruby например это частое явление)

Приходится выкручиваться через встраивание типов в родительские структуры для привязывания расширенных методов к локальному типу с сохранением возможности использования методов встроенного нелокального типа из стороннего пакета/библиотеки, что не очень удобно из-за дополнительного boxing/unboxing'а (особенно в позиции возврата типа в функции/методе) из-за встраивания и необходимости вложенности типов для использования методов расширений на встроенных типах

"Плоское лучше, чем вложенное" ("Flat is better than nested") гласит одно из заявлений манифеста Python - и это очень правильно!
Но Go при всём использовании плоских абстракций (композиции вместо иерархии типов) в языке всё таки отходит от этих правил при необходимости расширения функционала встроенных и сторонних типов, и при этом балансирует на грани для сохранения максимальной локализованности подобных расширений без перегрузки и изменения поведения встроенных и сторонних типов и перегрузки операторов, не позволяя подобные действия, т.к. все мы знаем, что перегрузка методов встроенных и сторонних типов и операторов черевата пагубными последствиями в виде изменения поведения приложения (семантики встроенных типов языка, его операторов), например при импорте такого кода (с перегрузкой методов) как внешней зависимости в проект (думаю всем кто работал достаточно например с Ruby знакома данная ситуация и порождаемая импортом кода с перегрузкой методов проблема)

Rust например позволяет и перегрузку операторов и расширение встроенных и сторонних типов, но делает её максимально локальной (как в прочем и всё остальное в языке Rust - максимально локальное, локальный анализ lifetime'ов например) на базе trait'ов (абстрактных типов, тайп-класоов, типажей) и их имплементоров для конкретных типов, в .т.ч. встроенных

#Go
#Golang

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment