Last active
April 27, 2020 13:34
-
-
Save andrcmdr/d0bb34aec060e8b88413487e7ba24e5a to your computer and use it in GitHub Desktop.
Daily Clock Drift Correction (DCDC)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]))) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!") | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!") | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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