Browse Source

Merge pull request #4107 from Feoramund/add-digit-count

Add `core:math.count_digits_of_base`
Jeroen van Rijn 11 months ago
parent
commit
17eb0b5ee0
2 changed files with 65 additions and 1 deletions
  1. 30 0
      core/math/math.odin
  2. 35 1
      tests/core/math/test_core_math.odin

+ 30 - 0
core/math/math.odin

@@ -2443,6 +2443,36 @@ hypot :: proc{
 	hypot_f64, hypot_f64le, hypot_f64be,
 }
 
+@(require_results)
+count_digits_of_base :: proc "contextless" (value: $T, $base: int) -> (digits: int) where intrinsics.type_is_integer(T) {
+	#assert(base >= 2, "base must be 2 or greater.")
+
+	value := value
+	when !intrinsics.type_is_unsigned(T) {
+		value = abs(value)
+	}
+
+	when base == 2 {
+		digits = max(1, 8 * size_of(T) - int(intrinsics.count_leading_zeros(value)))
+	} else when intrinsics.count_ones(base) == 1 {
+		free_bits := 8 * size_of(T) - int(intrinsics.count_leading_zeros(value))
+		digits, free_bits = divmod(free_bits, intrinsics.constant_log2(base))
+		if free_bits > 0 {
+			digits += 1
+		}
+		digits = max(1, digits)
+	} else {
+		digits = 1
+		base := cast(T)base
+		for value >= base {
+			value /= base
+			digits += 1
+		}
+	}
+
+	return
+}
+
 F16_DIG        :: 3
 F16_EPSILON    :: 0.00097656
 F16_GUARD      :: 0

+ 35 - 1
tests/core/math/test_core_math.odin

@@ -2,6 +2,7 @@
 package test_core_math
 
 import "core:math"
+import "core:strconv"
 import "core:testing"
 
 @test
@@ -1229,4 +1230,37 @@ test_large_tan :: proc(t: ^testing.T) {
 		f2 := math.tan(vf[i] + large)
 		testing.expectf(t, close(t, f1, f2), "math.tan(%.15g) = %.15g, want %.15g", vf[i]+large, f2, f1)
 	}
-}
+}
+
+@test
+test_count_digits :: proc(t: ^testing.T) {
+	_run_test :: proc(t: ^testing.T, $base: int) {
+		buf: [64]u8
+		for n in 0..<i64(base*base*base) {
+			count := math.count_digits_of_base(n, base)
+			str := strconv.append_int(buf[:], n, base)
+			if !testing.expectf(t,
+				len(str) == count,
+				"decimal %i in base-%i digit count is %i, does not match length %i of %q",
+				n, base, count, len(str), str) {
+				break
+			}
+		}
+	}
+
+	_run_test(t, 2)
+	_run_test(t, 3)
+	_run_test(t, 4)
+	_run_test(t, 5)
+	_run_test(t, 6)
+	_run_test(t, 7)
+	_run_test(t, 8)
+	_run_test(t, 9)
+	_run_test(t, 10)
+	_run_test(t, 11)
+	_run_test(t, 12)
+	_run_test(t, 13)
+	_run_test(t, 14)
+	_run_test(t, 15)
+	_run_test(t, 16)
+}