Skip to content

Instantly share code, notes, and snippets.

@maxpushka
Last active October 11, 2023 13:51
Show Gist options
  • Save maxpushka/4ffb180874d09c238775cee8d44cf6f4 to your computer and use it in GitHub Desktop.
Save maxpushka/4ffb180874d09c238775cee8d44cf6f4 to your computer and use it in GitHub Desktop.
float to significant figures converter
package digits
import "math"
func ToSignificant(x float64, significantDigits, maxDecimals uint) float64 {
if x == 0.0 {
return 0.0
}
intX := math.Floor(x)
fractionalPart := x - intX
var digitsBefore int
if intX != 0.0 {
digitsBefore = int(math.Log10(x)) + 1
}
if digitsBefore > int(significantDigits) {
power := math.Pow10(digitsBefore - int(significantDigits))
return math.Round(x/power) * power
} else if digitsBefore > 0 {
// Calculate number of significant digits left for the fractional part
significantDigitsLeft := int(significantDigits) - digitsBefore
// Extract and round the fractional part
power := math.Pow10(significantDigitsLeft)
fractionalPart = math.Round(fractionalPart*power) / power
return intX + fractionalPart
}
// Truncate decimals that are greater than max allowed decimals
power := math.Pow10(int(maxDecimals))
fractionalPart = math.Round(fractionalPart*power) / power
// Get number of leading zeros of fractional part
var leadingZeros int
for math.Floor(fractionalPart*10) == 0 && fractionalPart != 0 {
leadingZeros++
fractionalPart *= 10
}
// Get fractional part without leading zeros
power = math.Pow10(int(maxDecimals) - leadingZeros)
fractionalPart = math.Round(fractionalPart * power)
// Get number of digits in fractional part without leading zeros
numDigits := int(math.Log10(fractionalPart)) + 1
// Round fractional part
power = math.Pow10(numDigits - int(significantDigits))
fractionalPart = math.Round(fractionalPart / power)
// Append leading zeros
fractionalPart /= math.Pow10(int(significantDigits) + leadingZeros)
result := intX + fractionalPart
return result
}
package digits
import (
"fmt"
"math"
"testing"
)
func TestToSignificant(t *testing.T) {
tests := []struct {
input float64
expected float64
}{
{0.0, 0.0},
{0.000123456, 0.00012346},
{0.0001, 0.0001},
{0.012344789, 0.012345},
{0.012345, 0.012345},
{100006, 100010},
{math.SmallestNonzeroFloat64, 0.0},
{1.00000234, 1.0},
}
for _, tt := range tests {
tt := tt
t.Run(fmt.Sprintf("%f -> %f", tt.input, tt.expected), func(t *testing.T) {
actual := ToSignificant(tt.input, 5, 8)
if actual != tt.expected {
t.Errorf("ToSignificant(%f): expected %f, got %f", tt.input, tt.expected, actual)
}
})
}
}
func BenchmarkToSignificant_DecimalWithLeadingZeros(b *testing.B) {
// Reset timer to exclude time taken by setup operations
// before the actual benchmark begins
b.ResetTimer()
for i := 0; i < b.N; i++ {
ToSignificant(0.000123456, 5, 8)
}
}
func BenchmarkToSignificant_TruncateDecimals(b *testing.B) {
// Reset timer to exclude time taken by setup operations
// before the actual benchmark begins
b.ResetTimer()
for i := 0; i < b.N; i++ {
ToSignificant(1.00000234, 5, 8)
}
}
func BenchmarkToSignificant_IntegralPartSizeGreaterThanSignificantDigits(b *testing.B) {
// Reset timer to exclude time taken by setup operations
// before the actual benchmark begins
b.ResetTimer()
for i := 0; i < b.N; i++ {
ToSignificant(100006, 5, 8)
}
}
@mnaichuk
Copy link

0.0123467 - 0.012347
0.012345 - 0.012345
139746 - 139750
1.02356 - 1.0236
100006 - 100010

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