1044 lines
26 KiB
Go
1044 lines
26 KiB
Go
// Copyright 2018 The Periph Authors. All rights reserved.
|
|
// Use of this source code is governed under the Apache License, Version 2.0
|
|
// that can be found in the LICENSE file.
|
|
|
|
package physic
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// Angle is the measurement of the difference in orientation between two vectors
|
|
// stored as an int64 nano radian.
|
|
//
|
|
// A negative angle is valid.
|
|
//
|
|
// The highest representable value is a bit over 500,000,000,000°.
|
|
type Angle int64
|
|
|
|
// String returns the angle formatted as a string in degree.
|
|
func (a Angle) String() string {
|
|
// Angle is not a S.I. unit, so it must not be prefixed by S.I. prefixes.
|
|
if a == 0 {
|
|
return "0°"
|
|
}
|
|
// Round.
|
|
prefix := ""
|
|
if a < 0 {
|
|
a = -a
|
|
prefix = "-"
|
|
}
|
|
switch {
|
|
case a < Degree:
|
|
v := ((a * 1000) + Degree/2) / Degree
|
|
return prefix + "0." + prefixZeros(3, int(v)) + "°"
|
|
case a < 10*Degree:
|
|
v := ((a * 1000) + Degree/2) / Degree
|
|
i := v / 1000
|
|
v = v - i*1000
|
|
return prefix + strconv.FormatInt(int64(i), 10) + "." + prefixZeros(3, int(v)) + "°"
|
|
case a < 100*Degree:
|
|
v := ((a * 1000) + Degree/2) / Degree
|
|
i := v / 1000
|
|
v = v - i*1000
|
|
return prefix + strconv.FormatInt(int64(i), 10) + "." + prefixZeros(2, int(v)) + "°"
|
|
case a < 1000*Degree:
|
|
v := ((a * 1000) + Degree/2) / Degree
|
|
i := v / 1000
|
|
v = v - i*1000
|
|
return prefix + strconv.FormatInt(int64(i), 10) + "." + prefixZeros(1, int(v)) + "°"
|
|
default:
|
|
v := (a + Degree/2) / Degree
|
|
return prefix + strconv.FormatInt(int64(v), 10) + "°"
|
|
}
|
|
}
|
|
|
|
const (
|
|
NanoRadian Angle = 1
|
|
MicroRadian Angle = 1000 * NanoRadian
|
|
MilliRadian Angle = 1000 * MicroRadian
|
|
Radian Angle = 1000 * MilliRadian
|
|
|
|
// Theta is 2π. This is equivalent to 360°.
|
|
Theta Angle = 6283185307 * NanoRadian
|
|
Pi Angle = 3141592653 * NanoRadian
|
|
Degree Angle = 17453293 * NanoRadian
|
|
)
|
|
|
|
// Distance is a measurement of length stored as an int64 nano metre.
|
|
//
|
|
// This is one of the base unit in the International System of Units.
|
|
//
|
|
// The highest representable value is 9.2Gm.
|
|
type Distance int64
|
|
|
|
// String returns the distance formatted as a string in metre.
|
|
func (d Distance) String() string {
|
|
return nanoAsString(int64(d)) + "m"
|
|
}
|
|
|
|
const (
|
|
NanoMetre Distance = 1
|
|
MicroMetre Distance = 1000 * NanoMetre
|
|
MilliMetre Distance = 1000 * MicroMetre
|
|
Metre Distance = 1000 * MilliMetre
|
|
KiloMetre Distance = 1000 * Metre
|
|
MegaMetre Distance = 1000 * KiloMetre
|
|
GigaMetre Distance = 1000 * MegaMetre
|
|
|
|
// Conversion between Metre and imperial units.
|
|
Thou Distance = 25400 * NanoMetre
|
|
Inch Distance = 1000 * Thou
|
|
Foot Distance = 12 * Inch
|
|
Yard Distance = 3 * Foot
|
|
Mile Distance = 1760 * Yard
|
|
)
|
|
|
|
// ElectricCurrent is a measurement of a flow of electric charge stored as an
|
|
// int64 nano Ampere.
|
|
//
|
|
// This is one of the base unit in the International System of Units.
|
|
//
|
|
// The highest representable value is 9.2GA.
|
|
type ElectricCurrent int64
|
|
|
|
// String returns the current formatted as a string in Ampere.
|
|
func (e ElectricCurrent) String() string {
|
|
return nanoAsString(int64(e)) + "A"
|
|
}
|
|
|
|
const (
|
|
NanoAmpere ElectricCurrent = 1
|
|
MicroAmpere ElectricCurrent = 1000 * NanoAmpere
|
|
MilliAmpere ElectricCurrent = 1000 * MicroAmpere
|
|
Ampere ElectricCurrent = 1000 * MilliAmpere
|
|
KiloAmpere ElectricCurrent = 1000 * Ampere
|
|
MegaAmpere ElectricCurrent = 1000 * KiloAmpere
|
|
GigaAmpere ElectricCurrent = 1000 * MegaAmpere
|
|
)
|
|
|
|
// ElectricPotential is a measurement of electric potential stored as an int64
|
|
// nano Volt.
|
|
//
|
|
// The highest representable value is 9.2GV.
|
|
type ElectricPotential int64
|
|
|
|
// String returns the tension formatted as a string in Volt.
|
|
func (e ElectricPotential) String() string {
|
|
return nanoAsString(int64(e)) + "V"
|
|
}
|
|
|
|
const (
|
|
// Volt is W/A, kg⋅m²/s³/A.
|
|
NanoVolt ElectricPotential = 1
|
|
MicroVolt ElectricPotential = 1000 * NanoVolt
|
|
MilliVolt ElectricPotential = 1000 * MicroVolt
|
|
Volt ElectricPotential = 1000 * MilliVolt
|
|
KiloVolt ElectricPotential = 1000 * Volt
|
|
MegaVolt ElectricPotential = 1000 * KiloVolt
|
|
GigaVolt ElectricPotential = 1000 * MegaVolt
|
|
)
|
|
|
|
// ElectricResistance is a measurement of the difficulty to pass an electric
|
|
// current through a conductor stored as an int64 nano Ohm.
|
|
//
|
|
// The highest representable value is 9.2GΩ.
|
|
type ElectricResistance int64
|
|
|
|
// String returns the resistance formatted as a string in Ohm.
|
|
func (e ElectricResistance) String() string {
|
|
return nanoAsString(int64(e)) + "Ω"
|
|
}
|
|
|
|
const (
|
|
// Ohm is V/A, kg⋅m²/s³/A².
|
|
NanoOhm ElectricResistance = 1
|
|
MicroOhm ElectricResistance = 1000 * NanoOhm
|
|
MilliOhm ElectricResistance = 1000 * MicroOhm
|
|
Ohm ElectricResistance = 1000 * MilliOhm
|
|
KiloOhm ElectricResistance = 1000 * Ohm
|
|
MegaOhm ElectricResistance = 1000 * KiloOhm
|
|
GigaOhm ElectricResistance = 1000 * MegaOhm
|
|
)
|
|
|
|
// Force is a measurement of interaction that will change the motion of an
|
|
// object stored as an int64 nano Newton.
|
|
//
|
|
// A measurement of Force is a vector and has a direction but this unit only
|
|
// represents the magnitude. The orientation needs to be stored as a Quaternion
|
|
// independently.
|
|
//
|
|
// The highest representable value is 9.2TN.
|
|
type Force int64
|
|
|
|
// String returns the force formatted as a string in Newton.
|
|
func (f Force) String() string {
|
|
return nanoAsString(int64(f)) + "N"
|
|
}
|
|
|
|
const (
|
|
// Newton is kg⋅m/s².
|
|
NanoNewton Force = 1
|
|
MicroNewton Force = 1000 * NanoNewton
|
|
MilliNewton Force = 1000 * MicroNewton
|
|
Newton Force = 1000 * MilliNewton
|
|
KiloNewton Force = 1000 * Newton
|
|
MegaNewton Force = 1000 * KiloNewton
|
|
GigaNewton Force = 1000 * MegaNewton
|
|
|
|
EarthGravity Force = 9806650 * MicroNewton
|
|
|
|
// Conversion between Newton and imperial units.
|
|
// Pound is both a unit of mass and weight (force). The suffix Force is added
|
|
// to disambiguate the measurement it represents.
|
|
PoundForce Force = 4448221615261 * NanoNewton
|
|
)
|
|
|
|
// Frequency is a measurement of cycle per second, stored as an int32 micro
|
|
// Hertz.
|
|
//
|
|
// The highest representable value is 9.2THz.
|
|
type Frequency int64
|
|
|
|
// String returns the frequency formatted as a string in Hertz.
|
|
func (f Frequency) String() string {
|
|
return microAsString(int64(f)) + "Hz"
|
|
}
|
|
|
|
// Duration returns the duration of one cycle at this frequency.
|
|
func (f Frequency) Duration() time.Duration {
|
|
// Note: Duration() should have been named Period().
|
|
// TODO(maruel): Rounding should be fine-tuned.
|
|
return time.Second * time.Duration(Hertz) / time.Duration(f)
|
|
}
|
|
|
|
// PeriodToFrequency returns the frequency for a period of this interval.
|
|
func PeriodToFrequency(t time.Duration) Frequency {
|
|
return Frequency(time.Second) * Hertz / Frequency(t)
|
|
}
|
|
|
|
const (
|
|
// Hertz is 1/s.
|
|
MicroHertz Frequency = 1
|
|
MilliHertz Frequency = 1000 * MicroHertz
|
|
Hertz Frequency = 1000 * MilliHertz
|
|
KiloHertz Frequency = 1000 * Hertz
|
|
MegaHertz Frequency = 1000 * KiloHertz
|
|
GigaHertz Frequency = 1000 * MegaHertz
|
|
TeraHertz Frequency = 1000 * GigaHertz
|
|
)
|
|
|
|
// Mass is a measurement of mass stored as an int64 nano gram.
|
|
//
|
|
// This is one of the base unit in the International System of Units.
|
|
//
|
|
// The highest representable value is 9.2Gg.
|
|
type Mass int64
|
|
|
|
// String returns the mass formatted as a string in gram.
|
|
func (m Mass) String() string {
|
|
return nanoAsString(int64(m)) + "g"
|
|
}
|
|
|
|
const (
|
|
NanoGram Mass = 1
|
|
MicroGram Mass = 1000 * NanoGram
|
|
MilliGram Mass = 1000 * MicroGram
|
|
Gram Mass = 1000 * MilliGram
|
|
KiloGram Mass = 1000 * Gram
|
|
MegaGram Mass = 1000 * KiloGram
|
|
GigaGram Mass = 1000 * MegaGram
|
|
Tonne Mass = MegaGram
|
|
|
|
// Conversion between Gram and imperial units.
|
|
// Ounce is both a unit of mass, weight (force) or volume depending on
|
|
// context. The suffix Mass is added to disambiguate the measurement it
|
|
// represents.
|
|
OunceMass Mass = 28349523125 * NanoGram
|
|
// Pound is both a unit of mass and weight (force). The suffix Mass is added
|
|
// to disambiguate the measurement it represents.
|
|
PoundMass Mass = 16 * OunceMass
|
|
Slug Mass = 14593903 * MilliGram
|
|
)
|
|
|
|
// Pressure is a measurement of force applied to a surface per unit
|
|
// area (stress) stored as an int64 nano Pascal.
|
|
//
|
|
// The highest representable value is 9.2GPa.
|
|
type Pressure int64
|
|
|
|
// String returns the pressure formatted as a string in Pascal.
|
|
func (p Pressure) String() string {
|
|
return nanoAsString(int64(p)) + "Pa"
|
|
}
|
|
|
|
const (
|
|
// Pascal is N/m², kg/m/s².
|
|
NanoPascal Pressure = 1
|
|
MicroPascal Pressure = 1000 * NanoPascal
|
|
MilliPascal Pressure = 1000 * MicroPascal
|
|
Pascal Pressure = 1000 * MilliPascal
|
|
KiloPascal Pressure = 1000 * Pascal
|
|
MegaPascal Pressure = 1000 * KiloPascal
|
|
GigaPascal Pressure = 1000 * MegaPascal
|
|
)
|
|
|
|
// RelativeHumidity is a humidity level measurement stored as an int32 fixed
|
|
// point integer at a precision of 0.00001%rH.
|
|
//
|
|
// Valid values are between 0% and 100%.
|
|
type RelativeHumidity int32
|
|
|
|
// String returns the humidity formatted as a string.
|
|
func (r RelativeHumidity) String() string {
|
|
r /= MilliRH
|
|
frac := int(r % 10)
|
|
if frac == 0 {
|
|
return strconv.Itoa(int(r)/10) + "%rH"
|
|
}
|
|
if frac < 0 {
|
|
frac = -frac
|
|
}
|
|
return strconv.Itoa(int(r)/10) + "." + strconv.Itoa(frac) + "%rH"
|
|
}
|
|
|
|
const (
|
|
TenthMicroRH RelativeHumidity = 1 // 0.00001%rH
|
|
MicroRH RelativeHumidity = 10 * TenthMicroRH // 0.0001%rH
|
|
MilliRH RelativeHumidity = 1000 * MicroRH // 0.1%rH
|
|
PercentRH RelativeHumidity = 10 * MilliRH // 1%rH
|
|
)
|
|
|
|
// Speed is a measurement of magnitude of velocity stored as an int64 nano
|
|
// Metre per Second.
|
|
//
|
|
// The highest representable value is 9.2Gm/s.
|
|
type Speed int64
|
|
|
|
// String returns the speed formatted as a string in m/s.
|
|
func (s Speed) String() string {
|
|
return nanoAsString(int64(s)) + "m/s"
|
|
}
|
|
|
|
const (
|
|
// MetrePerSecond is m/s.
|
|
NanoMetrePerSecond Speed = 1
|
|
MicroMetrePerSecond Speed = 1000 * NanoMetrePerSecond
|
|
MilliMetrePerSecond Speed = 1000 * MicroMetrePerSecond
|
|
MetrePerSecond Speed = 1000 * MilliMetrePerSecond
|
|
KiloMetrePerSecond Speed = 1000 * MetrePerSecond
|
|
MegaMetrePerSecond Speed = 1000 * KiloMetrePerSecond
|
|
GigaMetrePerSecond Speed = 1000 * MegaMetrePerSecond
|
|
|
|
LightSpeed Speed = 299792458 * MetrePerSecond
|
|
|
|
KilometrePerHour Speed = 277777778 * NanoMetrePerSecond
|
|
MilePerHour Speed = 447040 * MicroMetrePerSecond
|
|
FootPerSecond Speed = 304800 * MicroMetrePerSecond
|
|
)
|
|
|
|
// Temperature is a measurement of hotness stored as a nano kelvin.
|
|
//
|
|
// Negative values are invalid.
|
|
//
|
|
// The highest representable value is 9.2GK.
|
|
type Temperature int64
|
|
|
|
// String returns the temperature formatted as a string in °Celsius.
|
|
func (t Temperature) String() string {
|
|
return nanoAsString(int64(t-ZeroCelsius)) + "°C"
|
|
}
|
|
|
|
const (
|
|
NanoKelvin Temperature = 1
|
|
MicroKelvin Temperature = 1000 * NanoKelvin
|
|
MilliKelvin Temperature = 1000 * MicroKelvin
|
|
Kelvin Temperature = 1000 * MilliKelvin
|
|
KiloKelvin Temperature = 1000 * Kelvin
|
|
MegaKelvin Temperature = 1000 * KiloKelvin
|
|
GigaKelvin Temperature = 1000 * MegaKelvin
|
|
|
|
// Conversion between Kelvin and Celsius.
|
|
ZeroCelsius Temperature = 273150 * MilliKelvin
|
|
MilliCelsius Temperature = MilliKelvin
|
|
Celsius Temperature = Kelvin
|
|
|
|
// Conversion between Kelvin and Fahrenheit.
|
|
ZeroFahrenheit Temperature = 255372 * MilliKelvin
|
|
MilliFahrenheit Temperature = 555555 * NanoKelvin
|
|
Fahrenheit Temperature = 555555555 * NanoKelvin
|
|
)
|
|
|
|
// Power is a measurement of power stored as a nano watts.
|
|
//
|
|
// The highest representable value is 9.2GW.
|
|
type Power int64
|
|
|
|
// String returns the power formatted as a string in watts.
|
|
func (p Power) String() string {
|
|
return nanoAsString(int64(p)) + "W"
|
|
}
|
|
|
|
const (
|
|
// Watt is unit of power J/s, kg⋅m²⋅s⁻³
|
|
NanoWatt Power = 1
|
|
MicroWatt Power = 1000 * NanoWatt
|
|
MilliWatt Power = 1000 * MicroWatt
|
|
Watt Power = 1000 * MilliWatt
|
|
KiloWatt Power = 1000 * Watt
|
|
MegaWatt Power = 1000 * KiloWatt
|
|
GigaWatt Power = 1000 * MegaWatt
|
|
)
|
|
|
|
// Energy is a measurement of work stored as a nano joules.
|
|
//
|
|
// The highest representable value is 9.2GJ.
|
|
type Energy int64
|
|
|
|
// String returns the energy formatted as a string in Joules.
|
|
func (e Energy) String() string {
|
|
return nanoAsString(int64(e)) + "J"
|
|
}
|
|
|
|
const (
|
|
// Joule is a unit of work. kg⋅m²⋅s⁻²
|
|
NanoJoule Energy = 1
|
|
MicroJoule Energy = 1000 * NanoJoule
|
|
MilliJoule Energy = 1000 * MicroJoule
|
|
Joule Energy = 1000 * MilliJoule
|
|
KiloJoule Energy = 1000 * Joule
|
|
MegaJoule Energy = 1000 * KiloJoule
|
|
GigaJoule Energy = 1000 * MegaJoule
|
|
)
|
|
|
|
// ElectricalCapacitance is a measurement of capacitance stored as a pico farad.
|
|
//
|
|
// The highest representable value is 9.2MF.
|
|
type ElectricalCapacitance int64
|
|
|
|
// String returns the energy formatted as a string in Farad.
|
|
func (c ElectricalCapacitance) String() string {
|
|
return picoAsString(int64(c)) + "F"
|
|
}
|
|
|
|
const (
|
|
// Farad is a unit of capacitance. kg⁻¹⋅m⁻²⋅s⁴A²
|
|
PicoFarad ElectricalCapacitance = 1
|
|
NanoFarad ElectricalCapacitance = 1000 * PicoFarad
|
|
MicroFarad ElectricalCapacitance = 1000 * NanoFarad
|
|
MilliFarad ElectricalCapacitance = 1000 * MicroFarad
|
|
Farad ElectricalCapacitance = 1000 * MilliFarad
|
|
KiloFarad ElectricalCapacitance = 1000 * Farad
|
|
MegaFarad ElectricalCapacitance = 1000 * KiloFarad
|
|
)
|
|
|
|
// LuminousIntensity is a measurement of the quantity of visible light energy
|
|
// emitted per unit solid angle with wavelength power weighted by a luminosity
|
|
// function which represents the human eye's response to different wavelengths.
|
|
// The CIE 1931 luminosity function is the SI standard for candela.
|
|
//
|
|
// LuminousIntensity is stored as nano candela.
|
|
//
|
|
// This is one of the base unit in the International System of Units.
|
|
//
|
|
// The highest representable value is 9.2Gcd.
|
|
type LuminousIntensity int64
|
|
|
|
// String returns the energy formatted as a string in Candela.
|
|
func (l LuminousIntensity) String() string {
|
|
return nanoAsString(int64(l)) + "cd"
|
|
}
|
|
|
|
const (
|
|
// Candela is a unit of luminous intensity. cd
|
|
NanoCandela LuminousIntensity = 1
|
|
MicroCandela LuminousIntensity = 1000 * NanoCandela
|
|
MilliCandela LuminousIntensity = 1000 * MicroCandela
|
|
Candela LuminousIntensity = 1000 * MilliCandela
|
|
KiloCandela LuminousIntensity = 1000 * Candela
|
|
MegaCandela LuminousIntensity = 1000 * KiloCandela
|
|
GigaCandela LuminousIntensity = 1000 * MegaCandela
|
|
)
|
|
|
|
// LuminousFlux is a measurement of total quantity of visible light energy
|
|
// emitted with wavelength power weighted by a luminosity function which
|
|
// represents a model of the human eye's response to different wavelengths.
|
|
// The CIE 1931 luminosity function is the standard for lumens.
|
|
//
|
|
// LuminousFlux is stored as nano lumens.
|
|
//
|
|
// The highest representable value is 9.2Glm.
|
|
type LuminousFlux int64
|
|
|
|
// String returns the energy formatted as a string in Lumens.
|
|
func (f LuminousFlux) String() string {
|
|
return nanoAsString(int64(f)) + "lm"
|
|
}
|
|
|
|
const (
|
|
// Lumen is a unit of luminous flux. cd⋅sr
|
|
NanoLumen LuminousFlux = 1
|
|
MicroLumen LuminousFlux = 1000 * NanoLumen
|
|
MilliLumen LuminousFlux = 1000 * MicroLumen
|
|
Lumen LuminousFlux = 1000 * MilliLumen
|
|
KiloLumen LuminousFlux = 1000 * Lumen
|
|
MegaLumen LuminousFlux = 1000 * KiloLumen
|
|
GigaLumen LuminousFlux = 1000 * MegaLumen
|
|
)
|
|
|
|
//
|
|
|
|
func prefixZeros(digits, v int) string {
|
|
// digits is expected to be around 2~3.
|
|
s := strconv.Itoa(v)
|
|
for len(s) < digits {
|
|
// O(n²) but since digits is expected to run 2~3 times at most, it doesn't
|
|
// matter.
|
|
s = "0" + s
|
|
}
|
|
return s
|
|
}
|
|
|
|
// nanoAsString converts a value in S.I. unit in a string with the predefined
|
|
// prefix.
|
|
func nanoAsString(v int64) string {
|
|
sign := ""
|
|
if v < 0 {
|
|
if v == -9223372036854775808 {
|
|
v++
|
|
}
|
|
sign = "-"
|
|
v = -v
|
|
}
|
|
var frac int
|
|
var base int
|
|
var precision int64
|
|
unit := ""
|
|
switch {
|
|
case v >= 999999500000000001:
|
|
precision = v % 1000000000000000
|
|
base = int(v / 1000000000000000)
|
|
if precision > 500000000000000 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = "G"
|
|
case v >= 999999500000001:
|
|
precision = v % 1000000000000
|
|
base = int(v / 1000000000000)
|
|
if precision > 500000000000 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = "M"
|
|
case v >= 999999500001:
|
|
precision = v % 1000000000
|
|
base = int(v / 1000000000)
|
|
if precision > 500000000 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = "k"
|
|
case v >= 999999501:
|
|
precision = v % 1000000
|
|
base = int(v / 1000000)
|
|
if precision > 500000 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = ""
|
|
case v >= 1000000:
|
|
precision = v % 1000
|
|
base = int(v / 1000)
|
|
if precision > 500 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = "m"
|
|
case v >= 1000:
|
|
frac = int(v) % 1000
|
|
base = int(v) / 1000
|
|
unit = "µ"
|
|
default:
|
|
if v == 0 {
|
|
return "0"
|
|
}
|
|
base = int(v)
|
|
unit = "n"
|
|
}
|
|
if frac == 0 {
|
|
return sign + strconv.Itoa(base) + unit
|
|
}
|
|
return sign + strconv.Itoa(base) + "." + prefixZeros(3, frac) + unit
|
|
}
|
|
|
|
// microAsString converts a value in S.I. unit in a string with the predefined
|
|
// prefix.
|
|
func microAsString(v int64) string {
|
|
sign := ""
|
|
if v < 0 {
|
|
if v == -9223372036854775808 {
|
|
v++
|
|
}
|
|
sign = "-"
|
|
v = -v
|
|
}
|
|
var frac int
|
|
var base int
|
|
var precision int64
|
|
unit := ""
|
|
switch {
|
|
case v >= 999999500000000001:
|
|
precision = v % 1000000000000000
|
|
base = int(v / 1000000000000000)
|
|
if precision > 500000000000000 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = "T"
|
|
case v >= 999999500000001:
|
|
precision = v % 1000000000000
|
|
base = int(v / 1000000000000)
|
|
if precision > 500000000000 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = "G"
|
|
case v >= 999999500001:
|
|
precision = v % 1000000000
|
|
base = int(v / 1000000000)
|
|
if precision > 500000000 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = "M"
|
|
case v >= 999999501:
|
|
precision = v % 1000000
|
|
base = int(v / 1000000)
|
|
if precision > 500000 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = "k"
|
|
case v >= 1000000:
|
|
precision = v % 1000
|
|
base = int(v / 1000)
|
|
if precision > 500 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = ""
|
|
case v >= 1000:
|
|
frac = int(v) % 1000
|
|
base = int(v) / 1000
|
|
unit = "m"
|
|
default:
|
|
if v == 0 {
|
|
return "0"
|
|
}
|
|
base = int(v)
|
|
unit = "µ"
|
|
}
|
|
if frac == 0 {
|
|
return sign + strconv.Itoa(base) + unit
|
|
}
|
|
return sign + strconv.Itoa(base) + "." + prefixZeros(3, frac) + unit
|
|
}
|
|
|
|
// picoAsString converts a value in S.I. unit in a string with the predefined
|
|
// prefix.
|
|
func picoAsString(v int64) string {
|
|
sign := ""
|
|
if v < 0 {
|
|
if v == -9223372036854775808 {
|
|
v++
|
|
}
|
|
sign = "-"
|
|
v = -v
|
|
}
|
|
var frac int
|
|
var base int
|
|
var precision int64
|
|
unit := ""
|
|
switch {
|
|
case v >= 999999500000000001:
|
|
precision = v % 1000000000000000
|
|
base = int(v / 1000000000000000)
|
|
if precision > 500000000000000 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = "M"
|
|
case v >= 999999500000001:
|
|
precision = v % 1000000000000
|
|
base = int(v / 1000000000000)
|
|
if precision > 500000000000 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = "k"
|
|
case v >= 999999500001:
|
|
precision = v % 1000000000
|
|
base = int(v / 1000000000)
|
|
if precision > 500000000 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = ""
|
|
case v >= 999999501:
|
|
precision = v % 1000000
|
|
base = int(v / 1000000)
|
|
if precision > 500000 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = "m"
|
|
case v >= 1000000:
|
|
precision = v % 1000
|
|
base = int(v / 1000)
|
|
if precision > 500 {
|
|
base++
|
|
}
|
|
frac = (base % 1000)
|
|
base = base / 1000
|
|
unit = "µ"
|
|
case v >= 1000:
|
|
frac = int(v) % 1000
|
|
base = int(v) / 1000
|
|
unit = "n"
|
|
default:
|
|
if v == 0 {
|
|
return "0"
|
|
}
|
|
base = int(v)
|
|
unit = "p"
|
|
}
|
|
if frac == 0 {
|
|
return sign + strconv.Itoa(base) + unit
|
|
}
|
|
return sign + strconv.Itoa(base) + "." + prefixZeros(3, frac) + unit
|
|
}
|
|
|
|
// Decimal is the exact representation of decimal number.
|
|
type decimal struct {
|
|
// digits hold the string representation of the significant decimal digits.
|
|
digits string
|
|
// exponent is the left or right decimal shift. (powers of ten).
|
|
exp int
|
|
// neg it true if the number is negative.
|
|
neg bool
|
|
}
|
|
|
|
// Positive powers of 10 in the form such that powerOF10[index] = 10^index.
|
|
var powerOf10 = [...]uint64{
|
|
1,
|
|
10,
|
|
100,
|
|
1000,
|
|
10000,
|
|
100000,
|
|
1000000,
|
|
10000000,
|
|
100000000,
|
|
1000000000,
|
|
10000000000,
|
|
100000000000,
|
|
1000000000000,
|
|
10000000000000,
|
|
100000000000000,
|
|
1000000000000000,
|
|
10000000000000000,
|
|
100000000000000000,
|
|
1000000000000000000,
|
|
}
|
|
|
|
// Maximum value for a int64.
|
|
const maxInt64 = (1<<63 - 1)
|
|
|
|
var maxUint64Str = "9223372036854775807"
|
|
|
|
// Converts from decimal to int64, using the decimal.digits character values and
|
|
// converting to a intermediate unit64.
|
|
// Scale is combined with the decimal exponent to maximise the resolution and is
|
|
// in powers of ten.
|
|
func dtoi(d decimal, scale int) (int64, error) {
|
|
// Use uint till the last as it allows checks for overflows.
|
|
var u uint64
|
|
for i := 0; i < len(d.digits); i++ {
|
|
// Check that is is a digit.
|
|
if d.digits[i] >= '0' && d.digits[i] <= '9' {
|
|
// '0' = 0x30 '1' = 0x31 ...etc.
|
|
digit := d.digits[i] - '0'
|
|
// *10 is decimal shift left.
|
|
u *= 10
|
|
check := u + uint64(digit)
|
|
// Check should always be larger than u unless we have overflowed.
|
|
// Similarly if check > max it will overflow when converted to int64.
|
|
if check < u || check > maxInt64 {
|
|
if d.neg {
|
|
return -maxInt64, &parseError{
|
|
s: "-" + maxUint64Str,
|
|
err: errors.New("overflows minimum is"),
|
|
}
|
|
}
|
|
return maxInt64, &parseError{
|
|
s: maxUint64Str,
|
|
err: errors.New("overflows maximum is"),
|
|
}
|
|
}
|
|
u = check
|
|
} else {
|
|
// Should not get here if used atod to generate the decimal.
|
|
return 0, &parseError{err: errors.New("not a number")}
|
|
}
|
|
}
|
|
|
|
// Get the total magnitude of the number.
|
|
// a^x * b^y = a*b^(x+y) since scale is of the order unity this becomes
|
|
// 1^x * b^y = b^(x+y).
|
|
// mag must be positive to use as index in to powerOf10 array.
|
|
mag := d.exp + scale
|
|
if mag < 0 {
|
|
mag *= -1
|
|
}
|
|
if mag > 18 {
|
|
return 0, errors.New("exponent exceeds int64")
|
|
}
|
|
// Divide is = 10^(-mag)
|
|
if d.exp+scale < 0 {
|
|
u = (u + powerOf10[mag]/2) / powerOf10[mag]
|
|
} else {
|
|
check := u * powerOf10[mag]
|
|
if check < u || check > maxInt64 {
|
|
if d.neg {
|
|
return -maxInt64, &parseError{
|
|
s: "-" + maxUint64Str,
|
|
err: errors.New("overflows minimum is"),
|
|
}
|
|
}
|
|
return maxInt64, &parseError{
|
|
s: maxUint64Str,
|
|
err: errors.New("overflows maximum is"),
|
|
}
|
|
}
|
|
u *= powerOf10[mag]
|
|
}
|
|
|
|
n := int64(u)
|
|
if d.neg {
|
|
n *= -1
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// Converts a string to a decimal form. The return int is how many bytes of the
|
|
// string are numeric. The string may contain +-0 prefixes and arbitrary
|
|
// suffixes as trailing non number characters are ignored.
|
|
// Significant digits are stored without leading or trailing zeros, rather an
|
|
// exponent is used.
|
|
func atod(s string) (decimal, int, error) {
|
|
var d decimal
|
|
start := 0
|
|
dp := 0
|
|
end := len(s)
|
|
seenDigit := false
|
|
seenZero := false
|
|
isPoint := false
|
|
seenPlus := false
|
|
|
|
// Strip leading zeros, +/- and mark DP.
|
|
for i := 0; i < len(s); i++ {
|
|
switch {
|
|
case s[i] == '-':
|
|
if seenDigit {
|
|
end = i
|
|
break
|
|
}
|
|
if seenPlus {
|
|
return decimal{}, 0, &parseError{
|
|
s: s,
|
|
err: errors.New("can't contain both plus and minus symbols"),
|
|
}
|
|
}
|
|
if d.neg {
|
|
return decimal{}, 0, &parseError{
|
|
s: s,
|
|
err: errors.New("multiple minus symbols"),
|
|
}
|
|
}
|
|
d.neg = true
|
|
start++
|
|
case s[i] == '+':
|
|
if seenDigit {
|
|
end = i
|
|
break
|
|
}
|
|
if d.neg {
|
|
return decimal{}, 0, &parseError{
|
|
s: s,
|
|
err: errors.New("can't contain both plus and minus symbols"),
|
|
}
|
|
}
|
|
if seenPlus {
|
|
return decimal{}, 0, &parseError{
|
|
s: s,
|
|
err: errors.New("multiple plus symbols"),
|
|
}
|
|
}
|
|
seenPlus = true
|
|
start++
|
|
case s[i] == '.':
|
|
if isPoint {
|
|
return decimal{}, 0, &parseError{
|
|
s: s,
|
|
err: errors.New("multiple decimal points"),
|
|
}
|
|
}
|
|
isPoint = true
|
|
dp = i
|
|
if !seenDigit {
|
|
start++
|
|
}
|
|
case s[i] == '0':
|
|
if !seenDigit {
|
|
start++
|
|
}
|
|
seenZero = true
|
|
case s[i] >= '1' && s[i] <= '9':
|
|
seenDigit = true
|
|
default:
|
|
if !seenDigit && !seenZero {
|
|
return decimal{}, 0, &parseError{
|
|
s: s,
|
|
err: errors.New("is not a number"),
|
|
}
|
|
}
|
|
end = i
|
|
break
|
|
}
|
|
}
|
|
|
|
last := end
|
|
seenDigit = false
|
|
exp := 0
|
|
// Strip non significant zeros to find base exponent.
|
|
for i := end - 1; i > start-1; i-- {
|
|
switch {
|
|
case s[i] >= '1' && s[i] <= '9':
|
|
seenDigit = true
|
|
case s[i] == '.':
|
|
if !seenDigit {
|
|
end--
|
|
}
|
|
case s[i] == '0':
|
|
if !seenDigit {
|
|
if i > dp {
|
|
end--
|
|
}
|
|
if i <= dp || dp == 0 {
|
|
exp++
|
|
}
|
|
}
|
|
default:
|
|
last--
|
|
end--
|
|
}
|
|
}
|
|
|
|
if dp > start && dp < end {
|
|
// Concatenate with out decimal point.
|
|
d.digits = s[start:dp] + s[dp+1:end]
|
|
} else {
|
|
d.digits = s[start:end]
|
|
}
|
|
if !isPoint {
|
|
d.exp = exp
|
|
} else {
|
|
ttl := dp - start
|
|
length := len(d.digits)
|
|
if ttl > 0 {
|
|
d.exp = ttl - length
|
|
} else {
|
|
d.exp = ttl - length + 1
|
|
}
|
|
}
|
|
return d, last, nil
|
|
}
|
|
|
|
type parseError struct {
|
|
s string
|
|
position int
|
|
err error
|
|
}
|
|
|
|
func (p *parseError) Error() string {
|
|
if p.err == nil {
|
|
return "parse error"
|
|
}
|
|
if p.s == "" {
|
|
return "parse error: " + p.err.Error()
|
|
}
|
|
return "parse error: " + p.err.Error() + ": \"" + p.s + "\""
|
|
}
|
|
|
|
func noUnits(s string) error {
|
|
return &parseError{s: s, err: errors.New("no units provided, need")}
|
|
}
|
|
|
|
type prefix int
|
|
|
|
const (
|
|
pico prefix = -12
|
|
nano prefix = -9
|
|
micro prefix = -6
|
|
milli prefix = -3
|
|
none prefix = 0
|
|
deca prefix = 1
|
|
hecto prefix = 2
|
|
kilo prefix = 3
|
|
mega prefix = 6
|
|
giga prefix = 9
|
|
tera prefix = 12
|
|
)
|
|
|
|
func parseSIPrefix(r rune) (prefix, int) {
|
|
switch r {
|
|
case 'p':
|
|
return pico, len("p")
|
|
case 'n':
|
|
return nano, len("n")
|
|
case 'u':
|
|
return micro, len("u")
|
|
case 'µ':
|
|
return micro, len("µ")
|
|
case 'm':
|
|
return milli, len("m")
|
|
case 'k':
|
|
return kilo, len("k")
|
|
case 'M':
|
|
return mega, len("M")
|
|
case 'G':
|
|
return giga, len("G")
|
|
case 'T':
|
|
return tera, len("T")
|
|
default:
|
|
return none, 0
|
|
}
|
|
}
|