This is an automated email from the ASF dual-hosted git repository.

leerho pushed a commit to branch improve_util_numdigits
in repository https://gitbox.apache.org/repos/asf/datasketches-java.git

commit fb2da3996eec7fea40d090b206fe12a668d23910
Author: Lee Rhodes <[email protected]>
AuthorDate: Mon Sep 7 17:02:51 2026 -0700

    Improve the numDigits method
---
 .../java/org/apache/datasketches/common/Util.java  | 66 ++++++++++++++--------
 .../org/apache/datasketches/common/UtilTest.java   | 16 ++++++
 2 files changed, 58 insertions(+), 24 deletions(-)

diff --git a/src/main/java/org/apache/datasketches/common/Util.java 
b/src/main/java/org/apache/datasketches/common/Util.java
index afb080501..42f28b427 100644
--- a/src/main/java/org/apache/datasketches/common/Util.java
+++ b/src/main/java/org/apache/datasketches/common/Util.java
@@ -620,7 +620,7 @@ public final class Util {
   }
 
   /** Logarithm related ************************/
-  
+
   /**
    * The log<sub>2</sub>(value)
    * @param value the given value
@@ -719,7 +719,7 @@ public final class Util {
   }
 
   /** Checks that throw ************************/
-  
+
   /**
    * Check the requested offset and length against the allocated size.
    * The invariants equation is: {@code 0 <= reqOff <= reqLen <= reqOff + 
reqLen <= allocSize}.
@@ -796,7 +796,7 @@ public final class Util {
   }
 
   /** Decimal Digits ***************************/
-  
+
   /**
    * Computes the number of decimal digits of the number n
    * @param n the given number
@@ -807,8 +807,26 @@ public final class Util {
     return (int) ceil(log(n) / log(10));
   }
 
+  /**
+   * Computes the minimum number of characters required to print the number n 
as a decimal.
+   * Negative numbers add one for the minus sign character.
+   * No other non-digit characters are assumed.
+   * @param n the given number, which may be negative.
+   * @return the number of characters required to print the number n
+   */
+  public static int numDigits2(long n) {
+      if (n == 0) { return 1; }    // handles the zero special case
+      int count = (n < 0) ? 1 : 0; // handles the minus sign
+      while (n != 0) {
+          n /= 10;
+          count++;
+      }
+      return count;
+  }
+
+
   /** Generic relational tests *****************/
-  
+
   /**
    * Finds the minimum of two generic items
    * @param <T> the type
@@ -971,7 +989,7 @@ public final class Util {
   }
 
   /** Seed Hashes ******************************/
-  
+
   /**
    * Computes and checks the 16-bit seed hash from the given long seed.
    * The seed hash may not be zero in order to maintain compatibility with 
older serialized
@@ -1004,25 +1022,25 @@ public final class Util {
     }
     return seedHashA;
   }
-  
+
   /** Files and File Bytes ******************************/
 
   /**
-   * Windows, POSIX, and JAR friendly, returns a byte array of the contents of 
the file defined by the given 
+   * Windows, POSIX, and JAR friendly, returns a byte array of the contents of 
the file defined by the given
    * resourceName.
    * If the resource is in a JAR it will be copied into the File System as a 
temporary file first.
-   * 
+   *
    * @param resourceName the short name or the full path name.
    * @return a byte array of the contents of the file defined by the given 
resourceName.
    */
   public static byte[] getResourceBytes(final String resourceName) {
     Objects.requireNonNull(resourceName, "Given resourceName must not be 
null");
-    
+
     String normalizedName = resourceName.replace('\\', '/');
     if (normalizedName.startsWith("/")) {
       normalizedName = normalizedName.substring(1);
     }
-  
+
     final ClassLoader loader = Util.class.getClassLoader();
     try (InputStream in = loader.getResourceAsStream(normalizedName)) {
       if (in == null) {
@@ -1037,10 +1055,10 @@ public final class Util {
   /**
    *   Windows, POSIX, and JAR friendly get Resource File.
    *   If the resource is in a JAR it will be copied into the File System as a 
temporary file first.
-   *   
-   *   <p>While tempFile.deleteOnExit() works, keep in mind that JVMs running 
long processes won't delete those files 
-   *   until the JVM completely terminates, which can lead to memory/disk 
leaks if this method is called frequently. 
-   *   If you're using this for memory-mapped files, consider explicitly 
deleting the file once the memory-mapped buffer 
+   *
+   *   <p>While tempFile.deleteOnExit() works, keep in mind that JVMs running 
long processes won't delete those files
+   *   until the JVM completely terminates, which can lead to memory/disk 
leaks if this method is called frequently.
+   *   If you're using this for memory-mapped files, consider explicitly 
deleting the file once the memory-mapped buffer
    *   is no longer in use, or registering a custom shutdown hook if 
deleteOnExit() is insufficient.</p>
    *   @param resourceName the simple file name or full path name.
    *   Any back-slashes will be converted to forward slashes and a leading 
forward slash will be removed.
@@ -1053,19 +1071,19 @@ public final class Util {
     // Normalize name: ClassLoaders MUST use forward slashes even on Windows
     String normalizedName = resourceName.replace('\\', '/');
     if (normalizedName.startsWith("/")) { normalizedName = 
normalizedName.substring(1); }
-  
+
     final ClassLoader loader = Util.class.getClassLoader();
     final URL url = loader.getResource(normalizedName);
     if (url == null) { throw new IllegalArgumentException("Resource not found: 
" + normalizedName); }
-  
+
     // If it's a real file, return it directly
     if ("file".equals(url.getProtocol())) {
-        try { 
+        try {
           final URI uri = url.toURI();
-          return new File(uri); } 
+          return new File(uri); }
         catch (final URISyntaxException e) { return new File(url.getPath()); }
     }
-  
+
     // If it's in a JAR, we must extract it for Memory.map() to work
     // We use a prefix that won't collide with Windows reserved names
     // We use NIO Files.createTempFile to ensure secure default permissions 
(0600)
@@ -1073,17 +1091,17 @@ public final class Util {
     try { tempFile = Files.createTempFile("datasketches-", ".bin").toFile(); }
     catch (final IOException e1) { throw new IllegalArgumentException(e1); }
     tempFile.deleteOnExit();
-  
+
     try (InputStream in = loader.getResourceAsStream(normalizedName)) {
         if (in == null) { throw new IllegalArgumentException("Could not open 
stream for " + normalizedName); }
-        
+
         // Use REPLACE_EXISTING to avoid "File Already Exists" errors on 
Windows retries
         Files.copy(in, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
     } catch (final IOException e) { throw new IllegalArgumentException(e); }
-  
+
     // Final Windows Fix: Ensure the file is actually writable if you need to 
setReadOnly later
-    //tempFile.setWritable(true); 
-    
+    //tempFile.setWritable(true);
+
     return tempFile;
   }
 
diff --git a/src/test/java/org/apache/datasketches/common/UtilTest.java 
b/src/test/java/org/apache/datasketches/common/UtilTest.java
index f9f0b09ae..22f20e274 100644
--- a/src/test/java/org/apache/datasketches/common/UtilTest.java
+++ b/src/test/java/org/apache/datasketches/common/UtilTest.java
@@ -46,6 +46,7 @@ import static org.apache.datasketches.common.Util.isPowerOf2;
 import static org.apache.datasketches.common.Util.longToBytes;
 import static org.apache.datasketches.common.Util.milliSecToString;
 import static org.apache.datasketches.common.Util.nanoSecToString;
+import static org.apache.datasketches.common.Util.numDigits2;
 import static org.apache.datasketches.common.Util.numberOfLeadingOnes;
 import static org.apache.datasketches.common.Util.numberOfTrailingOnes;
 import static org.apache.datasketches.common.Util.powerSeriesNextDouble;
@@ -433,7 +434,22 @@ public class UtilTest {
     assertEquals(v, v2);
   }
 
+  @Test
+  static void checkNumDigits() {
+    for (long n = 1; n < Long.MAX_VALUE && n > 0; n *= 10) {
+      checkN(n);
+      checkN(n - 1);
+      checkN(-n);
+      checkN(-n + 1);
+    }
+    checkN(Long.MAX_VALUE);
+    checkN(Long.MIN_VALUE);
+  }
 
+  private static void checkN(long n) {
+    //println(n);
+    assertEquals(numDigits2(n), String.valueOf(n).length());
+  }
 
   @Test
   public void printlnTest() {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to