Last active
October 11, 2023 13:51
-
-
Save maxpushka/4ffb180874d09c238775cee8d44cf6f4 to your computer and use it in GitHub Desktop.
float to significant figures converter
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 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 | |
} |
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 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) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
0.0123467 - 0.012347
0.012345 - 0.012345
139746 - 139750
1.02356 - 1.0236
100006 - 100010