Skip to content

Commit 8dfe482

Browse files
committed
speed up Double.toHexString
1 parent 3f11c43 commit 8dfe482

File tree

1 file changed

+55
-34
lines changed

1 file changed

+55
-34
lines changed

src/java.base/share/classes/java/lang/Double.java

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import jdk.internal.math.FloatingDecimal;
3434
import jdk.internal.math.DoubleConsts;
3535
import jdk.internal.math.DoubleToDecimal;
36+
import jdk.internal.util.DecimalDigits;
3637
import jdk.internal.vm.annotation.IntrinsicCandidate;
3738

3839
/**
@@ -707,51 +708,71 @@ public static String toHexString(double d) {
707708
// For infinity and NaN, use the decimal output.
708709
return Double.toString(d);
709710
else {
710-
// Initialized to maximum size of output.
711-
StringBuilder answer = new StringBuilder(24);
712-
713-
if (Math.copySign(1.0, d) == -1.0) // value is negative,
714-
answer.append("-"); // so append sign info
715-
716-
answer.append("0x");
717-
711+
// Handle sign
712+
boolean negative = Math.copySign(1.0, d) == -1.0;
718713
d = Math.abs(d);
719714

720-
if(d == 0.0) {
721-
answer.append("0.0p0");
715+
if (d == 0.0) {
716+
return negative ? "-0x0.0p0" : "0x0.0p0";
722717
} else {
718+
// Check if the value is subnormal (less than the smallest normal value)
723719
boolean subnormal = (d < Double.MIN_NORMAL);
724720

725721
// Isolate significand bits and OR in a high-order bit
726-
// so that the string representation has a known
727-
// length.
722+
// so that the string representation has a known length.
728723
long signifBits = (Double.doubleToLongBits(d)
729-
& DoubleConsts.SIGNIF_BIT_MASK) |
730-
0x1000000000000000L;
724+
& DoubleConsts.SIGNIF_BIT_MASK) |
725+
0x1000000000000000L;
726+
727+
// Calculate the number of trailing zeros in the significand (in groups of 4 bits)
728+
// This is used to remove trailing zeros from the hex representation
729+
int trailingZeros = Math.min(12, ((Long.numberOfTrailingZeros(signifBits) & 0xFC) >> 2));
730+
731+
// Determine the exponent value based on whether the number is subnormal or normal
732+
int exp = subnormal ? Double.MIN_EXPONENT : Math.getExponent(d);
733+
734+
// Calculate the total length of the resulting string
735+
int charlen = (negative ? 1 : 0) // sign character
736+
+ 4 // "0x1." or "0x0."
737+
+ 13 - trailingZeros // hex digits (13 max, minus trailing zeros)
738+
+ 1 // "p"
739+
+ DecimalDigits.stringSize(exp) // exponent
740+
;
741+
742+
// Create a byte array to hold the result characters
743+
byte[] chars = new byte[charlen];
744+
int index = 0;
745+
746+
// Add the sign character if the number is negative
747+
if (negative) { // value is negative
748+
chars[index++] = '-';
749+
}
750+
751+
// Add the prefix and the implicit bit ('1' for normal, '0' for subnormal)
752+
// Subnormal values have a 0 implicit bit; normal values have a 1 implicit bit.
753+
chars[index] = '0';
754+
chars[index + 1] = 'x';
755+
chars[index + 2] = (byte) (subnormal ? '0' : '1');
756+
chars[index + 3] = '.';
757+
index += 4;
758+
759+
// Convert significand to hex digits manually to avoid creating temporary strings
760+
// Extract the 13 hex digits (52 bits) from signifBits
761+
// We need to extract bits 48-51, 44-47, ..., 0-3 (13 groups of 4 bits)
762+
for (int i = 0, end = 13 - trailingZeros; i < end; i++) {
763+
// Extract 4 bits at a time from left to right
764+
// Shift right by (12 - i) * 4 positions and mask with 0xF
765+
chars[index++] = Integer.digits[((int)(signifBits >> ((12 - i) << 2))) & 0xF];
766+
}
731767

732-
// Subnormal values have a 0 implicit bit; normal
733-
// values have a 1 implicit bit.
734-
answer.append(subnormal ? "0." : "1.");
768+
// Add the exponent indicator
769+
chars[index] = 'p';
735770

736-
// Isolate the low-order 13 digits of the hex
737-
// representation. If all the digits are zero,
738-
// replace with a single 0; otherwise, remove all
739-
// trailing zeros.
740-
String signif = Long.toHexString(signifBits).substring(3,16);
741-
answer.append(signif.equals("0000000000000") ? // 13 zeros
742-
"0":
743-
signif.replaceFirst("0{1,12}$", ""));
771+
// Append the exponent value to the character array
772+
DecimalDigits.uncheckedGetCharsLatin1(exp, charlen, chars);
744773

745-
answer.append('p');
746-
// If the value is subnormal, use the E_min exponent
747-
// value for double; otherwise, extract and report d's
748-
// exponent (the representation of a subnormal uses
749-
// E_min -1).
750-
answer.append(subnormal ?
751-
Double.MIN_EXPONENT:
752-
Math.getExponent(d));
774+
return String.newStringWithLatin1Bytes(chars);
753775
}
754-
return answer.toString();
755776
}
756777
}
757778

0 commit comments

Comments
 (0)