robocar-led/vendor/periph.io/x/periph/conn/physic/units.go

2255 lines
58 KiB
Go
Raw Normal View History

2019-12-14 10:56:22 +00:00
// 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"
"strings"
"time"
"unicode/utf8"
)
// 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 9.223GRad or 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)) + "°"
case a > maxAngle-Degree:
u := (uint64(a) + uint64(Degree)/2) / uint64(Degree)
v := int64(u)
return prefix + strconv.FormatInt(int64(v), 10) + "°"
default:
v := (a + Degree/2) / Degree
return prefix + strconv.FormatInt(int64(v), 10) + "°"
}
}
// Set sets the Angle to the value represented by s. Units are to be provided in
// "rad", "deg" or "°" with an optional SI prefix: "p", "n", "u", "µ", "m", "k",
// "M", "G" or "T".
func (a *Angle) Set(s string) error {
d, n, err := atod(s)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s[n:], "Rad", "rad", "Deg", "deg", "°"); found != "" {
return err
}
return notNumberUnitErr("Rad, Deg or °")
case errOverflowsInt64:
// TODO(maruel): Look for suffix, and reuse it.
return maxValueErr(maxAngle.String())
case errOverflowsInt64Negative:
// TODO(maruel): Look for suffix, and reuse it.
return minValueErr(minAngle.String())
}
}
return err
}
var si prefix
if n != len(s) {
r, rsize := utf8.DecodeRuneInString(s[n:])
if r <= 1 || rsize == 0 {
return errors.New("unexpected end of string")
}
var siSize int
si, siSize = parseSIPrefix(r)
n += siSize
}
switch s[n:] {
case "Deg", "deg", "°":
degreePerRadian := decimal{
base: 17453293,
exp: 0,
neg: false,
}
deg, _ := decimalMul(d, degreePerRadian)
// Impossible for precision loss to exceed 9 since the number of
// significant figures in degrees per radian is only 8.
v, overflow := dtoi(deg, int(si))
if overflow {
if deg.neg {
return minValueErr(minAngle.String())
}
return maxValueErr(maxAngle.String())
}
*a = (Angle)(v)
case "Rad", "rad":
v, overflow := dtoi(d, int(si-nano))
if overflow {
if d.neg {
return minValueErr("-9.223G" + s[n:])
}
return maxValueErr("9.223G" + s[n:])
}
*a = (Angle)(v)
case "":
return noUnitErr("Rad, Deg or °")
default:
if found := hasSuffixes(s[n:], "Rad", "rad", "Deg", "deg", "°"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("Rad, Deg or °")
}
return nil
}
// Well known Angle constants.
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
maxAngle Angle = 9223372036854775807
minAngle Angle = -9223372036854775807
)
// 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"
}
// Set sets the Distance to the value represented by s. Units are to
// be provided in "m", "Mile", "Yard", "in", or "ft" with an optional SI
// prefix: "p", "n", "u", "µ", "m", "k", "M", "G" or "T".
func (d *Distance) Set(s string) error {
dc, n, err := atod(s)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s[n:], "in", "ft", "Yard", "yard", "Mile", "mile", "m"); found != "" {
return err
}
return notNumberUnitErr("m, Mile, in, ft or Yard")
case errOverflowsInt64:
// TODO(maruel): Look for suffix, and reuse it.
return maxValueErr(maxDistance.String())
case errOverflowsInt64Negative:
// TODO(maruel): Look for suffix, and reuse it.
return minValueErr(minDistance.String())
}
}
return err
}
si := prefix(unit)
if n != len(s) {
r, rsize := utf8.DecodeRuneInString(s[n:])
if r <= 1 || rsize == 0 {
return errors.New("unexpected end of string")
}
var siSize int
si, siSize = parseSIPrefix(r)
if si == milli || si == mega {
switch s[n:] {
case "m", "Mile", "mile":
si = unit
}
}
if si != unit {
n += siSize
}
}
v, overflow := dtoi(dc, int(si-nano))
if overflow {
if dc.neg {
return minValueErr(minDistance.String())
}
return maxValueErr(maxDistance.String())
}
switch s[n:] {
case "m":
*d = (Distance)(v)
case "Mile", "mile":
switch {
case v > maxMiles:
return maxValueErr("5731Mile")
case v < minMiles:
return minValueErr("-5731Mile")
case v >= 0:
*d = (Distance)((v*1609344 + 500) / 1000)
default:
*d = (Distance)((v*1609344 - 500) / 1000)
}
case "Yard", "yard":
switch {
case v > maxYards:
return maxValueErr("1 Million Yard")
case v < minYards:
return minValueErr("-1 Million Yard")
case v >= 0:
*d = (Distance)((v*9144 + 5000) / 10000)
default:
*d = (Distance)((v*9144 - 5000) / 10000)
}
case "ft":
switch {
case v > maxFeet:
return maxValueErr("3 Million ft")
case v < minFeet:
return minValueErr("-3 Million ft")
case v >= 0:
*d = (Distance)((v*3048 + 5000) / 10000)
default:
*d = (Distance)((v*3048 - 5000) / 10000)
}
case "in":
switch {
case v > maxInches:
return maxValueErr("36 Million inch")
case v < minInches:
return minValueErr("-36 Million inch")
case v >= 0:
*d = (Distance)((v*254 + 5000) / 10000)
default:
*d = (Distance)((v*254 - 5000) / 10000)
}
case "":
return noUnitErr("m, Mile, in, ft or Yard")
default:
if found := hasSuffixes(s[n:], "in", "ft", "Yard", "Mile", "m"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("m, Mile, in, ft or Yard")
}
return nil
}
// Well known Distance constants.
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
maxDistance = 9223372036854775807 * NanoMetre
minDistance = -9223372036854775807 * NanoMetre
maxMiles int64 = (int64(maxDistance) - 500) / int64((Mile)/1000000) // ~Max/1609344
minMiles int64 = (int64(minDistance) + 500) / int64((Mile)/1000000) // ~Min/1609344
maxYards int64 = (int64(maxDistance) - 5000) / int64((Yard)/100000) // ~Max/9144
minYards int64 = (int64(minDistance) + 5000) / int64((Yard)/100000) // ~Min/9144
maxFeet int64 = (int64(maxDistance) - 5000) / int64((Foot)/100000) // ~Max/3048
minFeet int64 = (int64(minDistance) + 5000) / int64((Foot)/100000) // ~Min/3048
maxInches int64 = (int64(maxDistance) - 5000) / int64((Inch)/100000) // ~Max/254
minInches int64 = (int64(minDistance) + 5000) / int64((Inch)/100000) // ~Min/254
)
// 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 (c ElectricCurrent) String() string {
return nanoAsString(int64(c)) + "A"
}
// Set sets the ElectricCurrent to the value represented by s. Units are to
// be provided in "A" with an optional SI prefix: "p", "n", "u", "µ", "m", "k",
// "M", "G" or "T".
func (c *ElectricCurrent) Set(s string) error {
v, n, err := valueOfUnitString(s, nano)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s, "A", "a"); found != "" {
return err
}
return notNumberUnitErr("A")
case errOverflowsInt64:
return maxValueErr(maxElectricCurrent.String())
case errOverflowsInt64Negative:
return minValueErr(minElectricCurrent.String())
}
}
return err
}
switch s[n:] {
case "A", "a":
*c = (ElectricCurrent)(v)
case "":
return noUnitErr("A")
default:
if found := hasSuffixes(s[n:], "A"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("A")
}
return nil
}
// Well known ElectricCurrent constants.
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
maxElectricCurrent = 9223372036854775807 * NanoAmpere
minElectricCurrent = -9223372036854775807 * NanoAmpere
)
// 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 (p ElectricPotential) String() string {
return nanoAsString(int64(p)) + "V"
}
// Set sets the ElectricPotential to the value represented by s. Units are to
// be provided in "V" with an optional SI prefix: "p", "n", "u", "µ", "m", "k",
// "M", "G" or "T".
func (p *ElectricPotential) Set(s string) error {
v, n, err := valueOfUnitString(s, nano)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s, "V", "v"); found != "" {
return err
}
return notNumberUnitErr("V")
case errOverflowsInt64:
return maxValueErr(maxElectricPotential.String())
case errOverflowsInt64Negative:
return minValueErr(minElectricPotential.String())
}
}
return err
}
switch s[n:] {
case "V", "v":
*p = (ElectricPotential)(v)
case "":
return noUnitErr("V")
default:
if found := hasSuffixes(s[n:], "V", "v"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("V")
}
return nil
}
// Well known ElectricPotential constants.
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
maxElectricPotential = 9223372036854775807 * NanoVolt
minElectricPotential = -9223372036854775807 * NanoVolt
)
// 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 (r ElectricResistance) String() string {
return nanoAsString(int64(r)) + "Ω"
}
// Set sets the ElectricResistance to the value represented by s. Units are to
// be provided in "Ohm", or "Ω" with an optional SI prefix: "p", "n", "u", "µ",
// "m", "k", "M", "G" or "T".
func (r *ElectricResistance) Set(s string) error {
v, n, err := valueOfUnitString(s, nano)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s, "Ohm", "ohm", "Ω"); found != "" {
return err
}
return notNumberUnitErr("Ohm or Ω")
case errOverflowsInt64:
return maxValueErr(maxElectricResistance.String())
case errOverflowsInt64Negative:
return minValueErr(minElectricResistance.String())
}
}
return err
}
switch s[n:] {
case "Ohm", "ohm", "Ω":
*r = (ElectricResistance)(v)
case "":
return noUnitErr("Ohm or Ω")
default:
if found := hasSuffixes(s[n:], "Ohm", "ohm", "Ω"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("Ohm or Ω")
}
return nil
}
// Well known ElectricResistance constants.
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
maxElectricResistance = 9223372036854775807 * NanoOhm
minElectricResistance = -9223372036854775807 * NanoOhm
)
// 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"
}
// Set sets the Force to the value represented by s. Units are to
// be provided in "N", or "lbf" (Pound force) with an optional SI prefix: "p",
// "n", "u", "µ", "m", "k", "M", "G" or "T".
func (f *Force) Set(s string) error {
d, n, err := atod(s)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s[n:], "N", "lbf"); found != "" {
return err
}
return notNumberUnitErr("N or lbf")
case errOverflowsInt64:
// TODO(maruel): Look for suffix, and reuse it.
return maxValueErr(maxForce.String())
case errOverflowsInt64Negative:
// TODO(maruel): Look for suffix, and reuse it.
return minValueErr(minForce.String())
}
}
return err
}
var si prefix
if n != len(s) {
r, rsize := utf8.DecodeRuneInString(s[n:])
if r <= 1 || rsize == 0 {
return errors.New("unexpected end of string")
}
var siSize int
si, siSize = parseSIPrefix(r)
n += siSize
}
switch s[n:] {
case "lbf":
poundForce := decimal{
base: 4448221615261,
exp: -3,
neg: false,
}
lbf, loss := decimalMul(d, poundForce)
if loss > 9 {
return errors.New("converting to nano Newtons would overflow, consider using nN for maximum precision")
}
v, overflow := dtoi(lbf, int(si))
if overflow {
if lbf.neg {
return minValueErr("-2.073496519Glbf")
}
return maxValueErr("2.073496519Glbf")
}
*f = (Force)(v)
case "N":
v, overflow := dtoi(d, int(si-nano))
if overflow {
if d.neg {
return minValueErr(minForce.String())
}
return maxValueErr(maxForce.String())
}
*f = (Force)(v)
case "":
return noUnitErr("N or lbf")
default:
if found := hasSuffixes(s[n:], "N", "lbf"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("N or lbf")
}
return nil
}
// Well known Force constants.
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 = 4448221615 * NanoNewton
maxForce Force = (1 << 63) - 1
minForce Force = -((1 << 63) - 1)
)
// Frequency is a measurement of cycle per second, stored as an int64 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"
}
// Set sets the Frequency to the value represented by s. Units are to
// be provided in "Hz" or "rpm" with an optional SI prefix: "p", "n", "u", "µ",
// "m", "k", "M", "G" or "T".
//
// Unlike most Set() functions, "Hz" is assumed by default.
func (f *Frequency) Set(s string) error {
v, n, err := valueOfUnitString(s, micro)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s, "Hz", "hz"); found != "" {
return err
}
return notNumberUnitErr("Hz")
case errOverflowsInt64:
return maxValueErr(maxFrequency.String())
case errOverflowsInt64Negative:
return minValueErr(minFrequency.String())
}
}
return err
}
switch s[n:] {
case "Hz", "hz", "":
*f = (Frequency)(v)
default:
if found := hasSuffixes(s[n:], "Hz", "hz"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("Hz")
}
return nil
}
// Period returns the duration of one cycle at this frequency.
//
// Frequency above GigaHertz cannot be represented as Duration.
//
// A 0Hz frequency returns a 0s period.
func (f Frequency) Period() time.Duration {
if f == 0 {
return 0
}
if f < 0 {
return (time.Second*time.Duration(Hertz) - time.Duration(f/2)) / time.Duration(f)
}
return (time.Second*time.Duration(Hertz) + time.Duration(f/2)) / time.Duration(f)
}
// Duration returns the duration of one cycle at this frequency.
//
// Deprecated: This method is removed in v4.0.0. Use Period() instead.
func (f Frequency) Duration() time.Duration {
return f.Period()
}
// PeriodToFrequency returns the frequency for a period of this interval.
//
// A 0s period returns a 0Hz frequency.
func PeriodToFrequency(p time.Duration) Frequency {
if p == 0 {
return 0
}
if p < 0 {
return (Frequency(time.Second)*Hertz - Frequency(p/2)) / Frequency(p)
}
return (Frequency(time.Second)*Hertz + Frequency(p/2)) / Frequency(p)
}
// Well known Frequency constants.
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
// RPM is revolutions per minute. It is used to quantify angular frequency.
RPM Frequency = 16667 * MicroHertz
maxFrequency = 9223372036854775807 * MicroHertz
minFrequency = -9223372036854775807 * MicroHertz
)
// 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"
}
// Set sets the Mass to the value represented by s. Units are to be provided in
// "g", "lb" or "oz" with an optional SI prefix: "p", "n", "u", "µ", "m", "k",
// "M", "G" or "T".
func (m *Mass) Set(s string) error {
d, n, err := atod(s)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s[n:], "g", "lb", "oz"); found != "" {
return err
}
return notNumberUnitErr("g, lb or oz")
case errOverflowsInt64:
// TODO(maruel): Look for suffix, and reuse it.
return maxValueErr(maxMass.String())
case errOverflowsInt64Negative:
// TODO(maruel): Look for suffix, and reuse it.
return minValueErr(minMass.String())
}
}
return err
}
var si prefix
if n != len(s) {
r, rsize := utf8.DecodeRuneInString(s[n:])
if r <= 1 || rsize == 0 {
return errors.New("unexpected end of string")
}
var siSize int
si, siSize = parseSIPrefix(r)
n += siSize
}
switch s[n:] {
case "g":
v, overflow := dtoi(d, int(si-nano))
if overflow {
if d.neg {
return minValueErr(minMass.String())
}
return maxValueErr(maxMass.String())
}
*m = (Mass)(v)
case "lb":
gramsPerlb := decimal{
base: uint64(PoundMass),
exp: 0,
neg: false,
}
lbs, _ := decimalMul(d, gramsPerlb)
v, overflow := dtoi(lbs, int(si))
if overflow {
if lbs.neg {
return minValueErr(strconv.FormatInt(int64(minPoundMass), 10) + "lb")
}
return maxValueErr(strconv.FormatInt(int64(maxPoundMass), 10) + "lb")
}
*m = (Mass)(v)
case "oz":
gramsPerOz := decimal{
base: uint64(OunceMass),
exp: 0,
neg: false,
}
oz, _ := decimalMul(d, gramsPerOz)
v, overflow := dtoi(oz, int(si))
if overflow {
if oz.neg {
return minValueErr(strconv.FormatInt(int64(minOunceMass), 10) + "oz")
}
return maxValueErr(strconv.FormatInt(int64(maxOunceMass), 10) + "oz")
}
*m = (Mass)(v)
case "":
return noUnitErr("g, lb or oz")
default:
if found := hasSuffixes(s[n:], "g", "lb", "oz"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("g, lb or oz")
}
return nil
}
// Well known Mass constants.
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
maxMass Mass = (1 << 63) - 1
minMass Mass = -((1 << 63) - 1)
// min and max Pound mass are in lb.
minPoundMass Mass = -20334054
maxPoundMass Mass = 20334054
// min and max Ounce mass are in oz.
minOunceMass Mass = -325344874
maxOunceMass Mass = 325344874
)
// 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"
}
// Set sets the Pressure to the value represented by s. Units are to
// be provided in "Pa" with an optional SI prefix: "p", "n", "u", "µ", "m", "k",
// "M", "G" or "T".
func (p *Pressure) Set(s string) error {
v, n, err := valueOfUnitString(s, nano)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s, "Pa"); found != "" {
return err
}
return notNumberUnitErr("Pa")
case errOverflowsInt64:
return maxValueErr(maxPressure.String())
case errOverflowsInt64Negative:
return minValueErr(minPressure.String())
}
}
return err
}
switch s[n:] {
case "Pa":
*p = (Pressure)(v)
case "":
return noUnitErr("Pa")
default:
if found := hasSuffixes(s[n:], "Pa"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("Pa")
}
return nil
}
// Well known Pressure constants.
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
maxPressure = 9223372036854775807 * NanoPascal
minPressure = -9223372036854775807 * NanoPascal
)
// 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"
}
// Set sets the RelativeHumidity to the value represented by s. Units are to
// be provided in "%rH" or "%" with an optional SI prefix: "p", "n", "u", "µ",
// "m", "k", "M", "G" or "T".
func (r *RelativeHumidity) Set(s string) error {
// PercentRH is micro + deca.
v, n, err := valueOfUnitString(s, micro+deca)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s[n:], "%rH", "%"); found != "" {
return err
}
return notNumberUnitErr("%rH or %")
case errOverflowsInt64:
return maxValueErr(maxRelativeHumidity.String())
case errOverflowsInt64Negative:
return minValueErr(minRelativeHumidity.String())
}
}
return err
}
switch s[n:] {
case "%rH", "%":
// We need an extra check here to make sure that v will fit inside a
// int32.
switch {
case v > int64(maxRelativeHumidity):
return maxValueErr(maxRelativeHumidity.String())
case v < int64(minRelativeHumidity):
return minValueErr(minRelativeHumidity.String())
}
*r = (RelativeHumidity)(v)
case "":
return noUnitErr("%rH or %")
default:
if found := hasSuffixes(s[n:], "%rH", "%"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("%rH or %")
}
return nil
}
// Well known RelativeHumidity constants.
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
maxRelativeHumidity RelativeHumidity = 100 * PercentRH
minRelativeHumidity RelativeHumidity = 0
)
// 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 (sp Speed) String() string {
return nanoAsString(int64(sp)) + "m/s"
}
// Set sets the Speed to the value represented by s. Units are to be provided in
// "mps"(meters per second), "m/s", "kph", "fps", or "mph" with an optional SI
// prefix: "p", "n", "u", "µ", "m", "k", "M", "G" or "T".
func (sp *Speed) Set(s string) error {
d, n, err := atod(s)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s[n:], "m/s", "mps", "kph", "fps", "mph"); found != "" {
return err
}
return notNumberUnitErr("m/s, mps, kph, fps or mph")
case errOverflowsInt64:
// TODO(maruel): Look for suffix, and reuse it.
return maxValueErr(maxSpeed.String())
case errOverflowsInt64Negative:
// TODO(maruel): Look for suffix, and reuse it.
return minValueErr(minSpeed.String())
}
}
return err
}
var si prefix
if n != len(s) {
r, rsize := utf8.DecodeRuneInString(s[n:])
if r <= 1 || rsize == 0 {
return errors.New("unexpected end of string")
}
var siSize int
si, siSize = parseSIPrefix(r)
if si == milli {
switch s[n:] {
case "m/s", "mps", "mph":
si = unit
siSize = 0
}
}
if si == kilo {
switch s[n:] {
case "kph":
si = unit
siSize = 0
}
}
n += siSize
}
switch s[n:] {
case "m/s", "mps":
v, overflow := dtoi(d, int(si-nano))
if overflow {
if d.neg {
return minValueErr(minSpeed.String())
}
return maxValueErr(maxSpeed.String())
}
*sp = (Speed)(v)
case "kph":
mpsPerkph := decimal{
base: uint64(KilometrePerHour),
exp: 0,
neg: false,
}
kph, _ := decimalMul(d, mpsPerkph)
v, overflow := dtoi(kph, int(si))
if overflow {
if kph.neg {
return minValueErr(strconv.FormatInt(int64(minKilometrePerHour), 10) + "kph")
}
return maxValueErr(strconv.FormatInt(int64(maxKilometrePerHour), 10) + "kph")
}
*sp = (Speed)(v)
case "fps":
mpsPerfps := decimal{
base: uint64(FootPerSecond / 1000),
exp: 3,
neg: false,
}
oz, _ := decimalMul(d, mpsPerfps)
v, overflow := dtoi(oz, int(si))
if overflow {
if oz.neg {
return minValueErr(strconv.FormatInt(int64(minFootPerSecond), 10) + "fps")
}
return maxValueErr(strconv.FormatInt(int64(maxFootPerSecond), 10) + "fps")
}
*sp = (Speed)(v)
case "mph":
mpsPermph := decimal{
base: uint64(MilePerHour / 1000),
exp: 3,
neg: false,
}
oz, _ := decimalMul(d, mpsPermph)
v, overflow := dtoi(oz, int(si))
if overflow {
if oz.neg {
return minValueErr(strconv.FormatInt(int64(minMilePerHour), 10) + "mph")
}
return maxValueErr(strconv.FormatInt(int64(maxMilePerHour), 10) + "mph")
}
*sp = (Speed)(v)
case "":
return noUnitErr("m/s, mps, kph, fps or mph")
default:
if found := hasSuffixes(s[n:], "m/s", "mps", "kph", "fps", "mph"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("m/s, mps, kph, fps or mph")
}
return nil
}
// Well known Speed constants.
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
maxSpeed Speed = (1 << 63) - 1
minSpeed Speed = -((1 << 63) - 1)
// Min Max KilometrePerHour are in kph.
minKilometrePerHour Speed = -33204139306
maxKilometrePerHour Speed = 33204139306
// Min Max MilePerHour are in mph.
minMilePerHour Speed = -20632095644
maxMilePerHour Speed = 20632095644
// Min Max FootPerSecond are in fps.
minFootPerSecond Speed = -30260406945
maxFootPerSecond Speed = 30260406945
)
// 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 {
if t < -ZeroCelsius || t > maxCelsius {
return nanoAsString(int64(t)) + "K"
}
return nanoAsString(int64(t-ZeroCelsius)) + "°C"
}
// Set sets the Temperature to the value represented by s. Units are to be
// provided in "C", "°C", "F", "°F" or "K" with an optional SI prefix: "p", "n",
// "u", "µ", "m", "k", "M", "G" or "T".
func (t *Temperature) Set(s string) error {
d, n, err := atod(s)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s[n:], "°C", "C", "°F", "F", "K"); found != "" {
return err
}
return notNumberUnitErr("K, °C, C, °F or F")
case errOverflowsInt64:
// TODO(maruel): Look for suffix, and reuse it.
return maxValueErr(maxTemperature.String())
case errOverflowsInt64Negative:
// TODO(maruel): Look for suffix, and reuse it.
return minValueErr(minTemperature.String())
}
}
return err
}
var si prefix
if n != len(s) {
r, rsize := utf8.DecodeRuneInString(s[n:])
if r <= 1 || rsize == 0 {
return errors.New("unexpected end of string")
}
var siSize int
si, siSize = parseSIPrefix(r)
n += siSize
}
switch s[n:] {
case "F", "°F":
// F to nK nK = 555555555.556*F + 255372222222
fPerK := decimal{
base: 555555555556,
exp: -3,
neg: false,
}
f, _ := decimalMul(d, fPerK)
v, overflow := dtoi(f, int(si))
if overflow {
if f.neg {
return minValueErr("-459.67F")
}
return maxValueErr(strconv.FormatInt(int64(maxFahrenheit), 10) + "F")
}
// We need an extra check here to make sure that will not overflow with
// the addition of ZeroFahrenheit.
switch {
case v > int64(maxTemperature-ZeroFahrenheit):
return maxValueErr(strconv.FormatInt(int64(maxFahrenheit), 10) + "F")
case v < int64(-ZeroFahrenheit):
return minValueErr("-459.67F")
}
v += int64(ZeroFahrenheit)
*t = (Temperature)(v)
case "K":
v, overflow := dtoi(d, int(si-nano))
if overflow {
if d.neg {
return minValueErr("0K")
}
return maxValueErr(strconv.FormatInt(int64(maxTemperature/1000000000), 10) + "K")
}
if v < 0 {
return minValueErr("0K")
}
*t = (Temperature)(v)
case "C", "°C":
v, overflow := dtoi(d, int(si-nano))
if overflow {
if d.neg {
return minValueErr("-273.15°C")
}
return maxValueErr(strconv.FormatInt(int64(maxCelsius/1000000000), 10) + "°C")
}
// We need an extra check here to make sure that will not overflow with
// the addition of ZeroCelsius.
switch {
case v > int64(maxCelsius):
return maxValueErr(strconv.FormatInt(int64(maxCelsius/1000000000), 10) + "°C")
case v < int64(-ZeroCelsius):
return minValueErr("-273.15°C")
}
v += int64(ZeroCelsius)
*t = (Temperature)(v)
case "":
return noUnitErr("K, °C, C, °F or F")
default:
if found := hasSuffixes(s[n:], "°C", "C", "°F", "F", "K"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("K, °C, C, °F or F")
}
return nil
}
// Celsius returns the temperature as a floating number of °Celsius.
func (t Temperature) Celsius() float64 {
return float64(t-ZeroCelsius) / float64(Celsius)
}
// Fahrenheit returns the temperature as a floating number of °Fahrenheit.
func (t Temperature) Fahrenheit() float64 {
return float64(t-ZeroFahrenheit) / float64(Fahrenheit)
}
// Well known Temperature constants.
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 = 255372222222 * NanoKelvin
MilliFahrenheit Temperature = 555555 * NanoKelvin
Fahrenheit Temperature = 555555555 * NanoKelvin
maxTemperature Temperature = (1 << 63) - 1
minTemperature Temperature = 0
// Maximum Celsius is 9223371763704775807°nC.
maxCelsius Temperature = maxTemperature - ZeroCelsius
// Maximum Fahrenheit is 16602069204F
maxFahrenheit Temperature = 16602069204
)
// 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"
}
// Set sets the Power to the value represented by s. Units are to
// be provided in "W" with an optional SI prefix: "p", "n", "u", "µ", "m", "k",
// "M", "G" or "T".
func (p *Power) Set(s string) error {
v, n, err := valueOfUnitString(s, nano)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s, "W", "w"); found != "" {
return err
}
return notNumberUnitErr("W")
case errOverflowsInt64:
return maxValueErr(maxPower.String())
case errOverflowsInt64Negative:
return minValueErr(minPower.String())
}
}
return err
}
switch s[n:] {
case "W", "w":
*p = (Power)(v)
case "":
return noUnitErr("W")
default:
if found := hasSuffixes(s[n:], "W", "w"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("W")
}
return nil
}
// Well known Power constants.
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
maxPower = 9223372036854775807 * NanoWatt
minPower = -9223372036854775807 * NanoWatt
)
// 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"
}
// Set sets the Energy to the value represented by s. Units are to
// be provided in "J" with an optional SI prefix: "p", "n", "u", "µ", "m", "k",
// "M", "G" or "T".
func (e *Energy) Set(s string) error {
v, n, err := valueOfUnitString(s, nano)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s, "J", "j"); found != "" {
return err
}
return notNumberUnitErr("J")
case errOverflowsInt64:
return maxValueErr(maxEnergy.String())
case errOverflowsInt64Negative:
return minValueErr(minEnergy.String())
}
}
return err
}
switch s[n:] {
case "J", "j":
*e = (Energy)(v)
case "":
return noUnitErr("J")
default:
if found := hasSuffixes(s[n:], "J", "j"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("J")
}
return nil
}
// Well known Energy constants.
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
// BTU (British thermal unit) is the heat required to raise the temperature
// of one pound of water by one degree Fahrenheit. This is the ISO value.
BTU Energy = 1055060 * MilliJoule
WattSecond Energy = Joule
WattHour Energy = 3600 * Joule
KiloWattHour Energy = 3600 * KiloJoule
maxEnergy = 9223372036854775807 * NanoJoule
minEnergy = -9223372036854775807 * NanoJoule
)
// 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"
}
// Set sets the ElectricalCapacitance to the value represented by s. Units are
// to be provided in "F" with an optional SI prefix: "p", "n", "u", "µ", "m",
// "k", "M", "G" or "T".
func (c *ElectricalCapacitance) Set(s string) error {
v, n, err := valueOfUnitString(s, pico)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s, "F", "f"); found != "" {
return err
}
return notNumberUnitErr("F")
case errOverflowsInt64:
return maxValueErr(maxElectricalCapacitance.String())
case errOverflowsInt64Negative:
return minValueErr(minElectricalCapacitance.String())
}
}
return err
}
switch s[n:] {
case "F", "f":
*c = (ElectricalCapacitance)(v)
case "":
return noUnitErr("F")
default:
if found := hasSuffixes(s[n:], "F", "f"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("F")
}
return nil
}
// Well known ElectricalCapacitance constants.
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
maxElectricalCapacitance = 9223372036854775807 * PicoFarad
minElectricalCapacitance = -9223372036854775807 * PicoFarad
)
// 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 (i LuminousIntensity) String() string {
return nanoAsString(int64(i)) + "cd"
}
// Set sets the LuminousIntensity to the value represented by s. Units are to
// be provided in "cd" with an optional SI prefix: "p", "n", "u", "µ", "m", "k",
// "M", "G" or "T".
func (i *LuminousIntensity) Set(s string) error {
v, n, err := valueOfUnitString(s, nano)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s, "cd"); found != "" {
return err
}
return notNumberUnitErr("cd")
case errOverflowsInt64:
return maxValueErr(maxLuminousIntensity.String())
case errOverflowsInt64Negative:
return minValueErr(minLuminousIntensity.String())
}
}
return err
}
switch s[n:] {
case "cd":
*i = (LuminousIntensity)(v)
case "":
return noUnitErr("cd")
default:
if found := hasSuffixes(s[n:], "cd"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("cd")
}
return nil
}
// Well known LuminousIntensity constants.
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
maxLuminousIntensity = 9223372036854775807 * NanoCandela
minLuminousIntensity = -9223372036854775807 * NanoCandela
)
// 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"
}
// Set sets the LuminousFlux to the value represented by s. Units are to
// be provided in "lm" with an optional SI prefix: "p", "n", "u", "µ", "m", "k",
// "M", "G" or "T".
func (f *LuminousFlux) Set(s string) error {
v, n, err := valueOfUnitString(s, nano)
if err != nil {
if e, ok := err.(*parseError); ok {
switch e.error {
case errNotANumber:
if found := hasSuffixes(s, "lm"); found != "" {
return err
}
return notNumberUnitErr("lm")
case errOverflowsInt64:
return maxValueErr(maxLuminousFlux.String())
case errOverflowsInt64Negative:
return minValueErr(minLuminousFlux.String())
}
}
return err
}
switch s[n:] {
case "lm":
*f = (LuminousFlux)(v)
case "":
return noUnitErr("lm")
default:
if found := hasSuffixes(s[n:], "lm"); found != "" {
return unknownUnitPrefixErr(found, "p,n,u,µ,m,k,M,G or T")
}
return incorrectUnitErr("lm")
}
return nil
}
// Well known LuminousFlux constants.
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
maxLuminousFlux = 9223372036854775807 * NanoLumen
minLuminousFlux = -9223372036854775807 * NanoLumen
)
//
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 representation of decimal number.
type decimal struct {
// base hold the significant digits.
base uint64
// 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 maxInt64Str = "9223372036854775807"
var (
errOverflowsInt64 = errors.New("exceeds maximum")
errOverflowsInt64Negative = errors.New("exceeds minimum")
errNotANumber = errors.New("not a number")
)
// Converts from decimal to int64.
//
// Scale is combined with the decimal exponent to maximise the resolution and is
// in powers of ten.
//
// Returns true if the value overflowed.
func dtoi(d decimal, scale int) (int64, bool) {
// 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.
u := d.base
mag := d.exp + scale
if mag < 0 {
mag = -mag
}
var n int64
if mag > 18 {
return 0, true
}
// Divide is = 10^(-mag)
switch {
case d.exp+scale < 0:
u = (u + powerOf10[mag]/2) / powerOf10[mag]
break
case mag == 0:
if u > maxInt64 {
return 0, true
}
break
default:
check := u * powerOf10[mag]
if check/powerOf10[mag] != u || check > maxInt64 {
return 0, true
}
u *= powerOf10[mag]
}
n = int64(u)
if d.neg {
n = -n
}
return n, false
}
// Converts a string to a decimal form. The return int is how many bytes of the
// string are considered 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 a
// base and exponent is used. Significant digits are stored as uint64, max size
// of significant digits is int64
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{
errors.New("contains both plus and minus symbols"),
}
}
if d.neg {
return decimal{}, 0, &parseError{
errors.New("contains multiple minus symbols"),
}
}
d.neg = true
start++
case s[i] == '+':
if seenDigit {
end = i
break
}
if d.neg {
return decimal{}, 0, &parseError{
errors.New("contains both plus and minus symbols"),
}
}
if seenPlus {
return decimal{}, 0, &parseError{
errors.New("contains multiple plus symbols"),
}
}
seenPlus = true
start++
case s[i] == '.':
if isPoint {
return decimal{}, 0, &parseError{
errors.New("contains 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{errNotANumber}
}
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--
}
}
for i := start; i < end; i++ {
c := s[i]
// Check that is is a digit.
if c >= '0' && c <= '9' {
// *10 is decimal shift left.
d.base *= 10
// Convert ascii digit into number.
check := d.base + uint64(c-'0')
// Check should always be larger than u unless we have overflowed.
// Similarly if check > max it will overflow when converted to int64.
if check < d.base || check > maxInt64 {
if d.neg {
return decimal{}, 0, &parseError{errOverflowsInt64Negative}
}
return decimal{}, 0, &parseError{errOverflowsInt64}
}
d.base = check
} else if c != '.' {
return decimal{}, 0, &parseError{errNotANumber}
}
}
if !isPoint {
d.exp = exp
} else {
if dp > start && dp < end {
// Decimal Point is in the middle of a number.
end--
}
// Find the exponent based on decimal point distance from left and the
// length of the number.
d.exp = (dp - start) - (end - start)
if dp <= start {
// Account for numbers of the form 1 > n < -1 eg 0.0001.
d.exp++
}
}
return d, last, nil
}
// valueOfUnitString is a helper for converting a string and a prefix in to a
// physic unit. It can be used when characters of the units do not conflict with
// any of the SI prefixes.
func valueOfUnitString(s string, base prefix) (int64, int, error) {
d, n, err := atod(s)
if err != nil {
return 0, n, err
}
si := prefix(unit)
if n != len(s) {
r, rsize := utf8.DecodeRuneInString(s[n:])
if r <= 1 || rsize == 0 {
return 0, 0, &parseError{
errors.New("unexpected end of string"),
}
}
var siSize int
si, siSize = parseSIPrefix(r)
n += siSize
}
v, overflow := dtoi(d, int(si-base))
if overflow {
if d.neg {
return -maxInt64, 0, &parseError{errOverflowsInt64Negative}
}
return maxInt64, 0, &parseError{errOverflowsInt64}
}
return v, n, nil
}
// decimalMul calcululates the product of two decimals; a and b, keeping the
// base less than maxInt64. Returns the number of times a figure was trimmed
// from either base coefficients. This function is to aid in the multiplication
// of numbers whose product have more than 18 significant figures. The minimum
// accuracy of the end product that has been truncated is 9 significant figures.
func decimalMul(a, b decimal) (decimal, uint) {
switch {
case a.base == 0 || b.base == 0:
// Anything multiplied by zero is zero. Special case to set exponent to
// zero.
return decimal{}, 0
case a.base > (1<<64)-6 || b.base > (1<<64)-6:
// In normal usage base will never be greater than 1<<63. However since
// base could be large as (1<<64 -1) this is to prevent an infinite loop
// when ((1<<64)-6)+5 overflows in the truncate least significant digit
// loop during rounding without adding addition bounds checking at that
// point.
break
default:
exp := a.exp + b.exp
neg := a.neg != b.neg
ab := a.base
bb := b.base
for i := uint(0); i < 21; i++ {
if ab <= 1 || bb <= 1 {
// This will always fit inside uint64.
return decimal{ab * bb, exp, neg}, i
}
if base := ab * bb; (base/ab == bb) && base < maxInt64 {
// Return if product did not overflow or exceed int64.
return decimal{base, exp, neg}, i
}
// Truncate least significant digit in product.
if bb > ab {
bb = (bb + 5) / 10
// Compact trailing zeros if any.
for bb > 0 && bb%10 == 0 {
bb /= 10
exp++
}
} else {
ab = (ab + 5) / 10
// Compact trailing zeros if any.
for ab > 0 && ab%10 == 0 {
ab /= 10
exp++
}
}
exp++
}
}
return decimal{}, 21
}
// hasSuffixes returns the first suffix found and the prefix content.
func hasSuffixes(s string, suffixes ...string) string {
for _, suffix := range suffixes {
if strings.HasSuffix(s, suffix) {
return suffix
}
}
return ""
}
type parseError struct {
error
}
func noUnitErr(valid string) error {
return errors.New("no unit provided; need " + valid)
}
func incorrectUnitErr(valid string) error {
return errors.New("unknown unit provided; need " + valid)
}
func unknownUnitPrefixErr(unit, valid string) error {
return errors.New("unknown unit prefix; valid prefixes for \"" + unit + "\" are " + valid)
}
func maxValueErr(valid string) error {
return errors.New("maximum value is " + valid)
}
func minValueErr(valid string) error {
return errors.New("minimum value is " + valid)
}
func notNumberUnitErr(unit string) error {
return errors.New("does not contain number or unit " + unit)
}
type prefix int
const (
pico prefix = -12
nano prefix = -9
micro prefix = -6
milli prefix = -3
unit 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 unit, 0
}
}