Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
/**
* Benchmarks for formatting the Knuth sampling rate (_dd.p.ksr tag value).
*
* <p>The format requirement is %.6g semantics: 6 significant figures, no trailing zeros, using
* fixed notation for values in [1e-4, 1] and scientific notation for smaller values.
* <p>The format requirement is 6 decimal digits of precision, no trailing zeros (fixed notation
* only; values below 0.0000005 round to "0").
*
* <p>Run with:
*
Expand Down Expand Up @@ -94,10 +94,12 @@ public void updateRateFreshTrace(Blackhole bh) {
bh.consume(ptags.getKnuthSamplingRateTagValue());
}

// ---- old implementation for comparison ----
// ---- old implementation for comparison (%.6f with trailing zero removal) ----

static String stringFormatImpl(double rate) {
String formatted = String.format(Locale.ROOT, "%.6g", rate);
if (rate <= 0.0) return "0";
if (rate >= 1.0) return "1";
String formatted = String.format(Locale.ROOT, "%.6f", rate);
int dotIndex = formatted.indexOf('.');
if (dotIndex >= 0) {
int end = formatted.length();
Expand All @@ -109,6 +111,6 @@ static String stringFormatImpl(double rate) {
}
formatted = formatted.substring(0, end);
}
return formatted;
return "0".equals(formatted) ? "0" : formatted;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -298,57 +298,28 @@ public void updateKnuthSamplingRate(double rate) {
}

/**
* Formats a sampling rate with up to 6 significant digits and no trailing zeros, matching
* {@code %.6g} semantics (fixed notation for values in [1e-4, 1], scientific for smaller).
* Formats a sampling rate with up to 6 decimal digits of precision and no trailing zeros.
*
* <p>Values below 0.0000005 (which round to zero at 6 decimal places) return {@code "0"}.
* Values at or above 0.9999995 return {@code "1"}.
*
* <p>Uses char-array arithmetic to avoid {@link java.util.Formatter} allocations entirely.
*/
static String formatKnuthSamplingRate(double rate) {
if (rate <= 0.0) return "0";
if (rate >= 1.0) return "1";

if (rate < 1e-4) {
return formatScientific6g(rate);
}

return formatFixed6g(rate);
}

/** Fixed notation for rates in [1e-4, 1): "0.DDDDDDDDD" with trailing zeros trimmed. */
private static String formatFixed6g(double rate) {
// Choose a multiplier so Math.round(rate * multiplier) is a 6-significant-figure integer.
// For rate in [10^-k, 10^-(k-1)) the first sig fig is at decimal position k, so we need
// k+5 total fractional digits:
// [0.1, 1.0) -> scale=6, multiplier=1e6
// [0.01, 0.1) -> scale=7, multiplier=1e7
// [0.001, 0.01) -> scale=8, multiplier=1e8
// [1e-4, 0.001) -> scale=9, multiplier=1e9
final int scale;
final long multiplier;
if (rate >= 0.1) {
scale = 6;
multiplier = 1_000_000L;
} else if (rate >= 0.01) {
scale = 7;
multiplier = 10_000_000L;
} else if (rate >= 0.001) {
scale = 8;
multiplier = 100_000_000L;
} else {
scale = 9;
multiplier = 1_000_000_000L;
}

long rounded = Math.round(rate * multiplier);
// Round to 6 decimal places.
long rounded = Math.round(rate * 1_000_000L);
if (rounded == 0) return "0";
if (rounded >= multiplier) return "1"; // rounding pushed value to 1.0
if (rounded >= 1_000_000L) return "1";

// Build "0." + <scale digits> and trim trailing zeros in a single right-to-left pass.
char[] buf = new char[2 + scale];
// Build "0.DDDDDD" and trim trailing zeros in a single right-to-left pass.
char[] buf = new char[8]; // "0." + 6 digits
buf[0] = '0';
buf[1] = '.';
int end = 2; // exclusive end; updated on first non-zero digit found from the right
for (int i = 2 + scale - 1; i >= 2; i--) {
for (int i = 7; i >= 2; i--) {
int d = (int) (rounded % 10);
rounded /= 10;
buf[i] = (char) ('0' + d);
Expand All @@ -360,72 +331,6 @@ private static String formatFixed6g(double rate) {
return new String(buf, 0, end);
}

/** Scientific notation for rates below 1e-4: "X.XXXXXe-YY" with mantissa zeros trimmed. */
private static String formatScientific6g(double rate) {
// Normalize to [1, 10) by repeated multiply — at most ~15 iterations for realistic rates.
int exp = 0;
double normalized = rate;
while (normalized < 1.0) {
normalized *= 10;
exp--;
}

// Round mantissa to 6 significant figures (integer in [100000, 999999]).
long sig = Math.round(normalized * 100000.0);
if (sig >= 1000000) {
sig /= 10;
exp++;
if (exp >= -4) {
// Rounding pushed the value into fixed-notation range (always exactly 0.0001).
return "0.0001";
}
}

// Build "X.XXXXXe-YY" trimming mantissa trailing zeros.
// Max: "X.XXXXXe-XXX" = 13 chars.
char[] buf = new char[13];
int pos = 0;

// Integer part (always a single digit 1-9).
buf[pos++] = (char) ('0' + (int) (sig / 100000));
sig %= 100000;

// Fractional part (5 digits, trim trailing zeros).
if (sig > 0) {
buf[pos++] = '.';
char[] frac = new char[5];
int fracEnd = 0;
for (int i = 4; i >= 0; i--) {
frac[i] = (char) ('0' + (int) (sig % 10));
sig /= 10;
if (frac[i] != '0' && fracEnd == 0) {
fracEnd = i + 1;
}
}
for (int i = 0; i < fracEnd; i++) {
buf[pos++] = frac[i];
}
}

// Exponent: "e-YY" (always negative here, at least 2 digits).
buf[pos++] = 'e';
buf[pos++] = '-';
int absExp = -exp;
if (absExp < 10) {
buf[pos++] = '0';
buf[pos++] = (char) ('0' + absExp);
} else if (absExp < 100) {
buf[pos++] = (char) ('0' + absExp / 10);
buf[pos++] = (char) ('0' + absExp % 10);
} else {
buf[pos++] = (char) ('0' + absExp / 100);
buf[pos++] = (char) ('0' + (absExp / 10) % 10);
buf[pos++] = (char) ('0' + absExp % 10);
}

return new String(buf, 0, pos);
}

TagValue getKnuthSamplingRateTagValue() {
return knuthSamplingRateTagValue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,30 @@ class KnuthSamplingRateTest extends DDCoreSpecification {
0.123456d | "0.123456"
0.100000d | "0.1"
0.250d | "0.25"
// [0.01, 0.1) magnitude bucket (scale=7)
0.05d | "0.05"
0.0123456789d | "0.0123457"
// [0.001, 0.01) magnitude bucket (scale=8)
// 6 decimal places: round(0.0123456789 * 1e6) = round(12345.6789) = 12346
0.0123456789d | "0.012346"
0.001d | "0.001"
0.00500d | "0.005"
0.00123456789d | "0.00123457"
// [0.0001, 0.001) magnitude bucket (scale=9)
// 6 decimal places: round(0.00123456789 * 1e6) = round(1234.56789) = 1235
0.00123456789d | "0.001235"
0.0001d | "0.0001"
0.000500d | "0.0005"
0.000123456789d | "0.000123457"
// rounding boundary: 0.9999995 rounds up to 1.0
// 6 decimal places: round(0.000123456789 * 1e6) = round(123.456789) = 123
0.000123456789d | "0.000123"
// rounding boundary: round(0.9999995 * 1e6) = round(999999.5) = 1000000 >= 1e6 -> "1"
0.9999995d | "1"
// scientific notation (rate < 1e-4)
0.00001d | "1e-05"
0.000050d | "5e-05"
1.23456789e-5d | "1.23457e-05"
1e-7d | "1e-07"
5.5e-10d | "5.5e-10"
// values in (0, 1e-4): fixed 6 decimal places, no scientific notation
0.00001d | "0.00001"
0.000050d | "0.00005"
// round(1.23456789e-5 * 1e6) = round(12.3456789) = 12
1.23456789e-5d | "0.000012"
// below 6-decimal-place precision: round to 0
1e-7d | "0"
5.5e-10d | "0"
// system-tests Test_Knuth_Sample_Rate boundary cases
0.000001d | "0.000001" // six_decimal_precision_boundary
0.00000051d | "0.000001" // rounds_up_to_one_millionth
}

def "agent rate sampler sets ksr propagated tag"() {
Expand Down
Loading