Skip to content

SR-3131: Adjust choice of decimal vs. exponential format #15805

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 12, 2018
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
15 changes: 11 additions & 4 deletions stdlib/public/runtime/SwiftDtoa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,9 @@ size_t swift_format_float(float d, char *dest, size_t length)
int8_t digits[9];
int digitCount =
swift_decompose_float(d, digits, sizeof(digits), &decimalExponent);
if (decimalExponent < -3 || decimalExponent > 6) {
// People use float to model integers <= 2^24, so we use that
// as a cutoff for decimal vs. exponential format.
if (decimalExponent < -3 || fabsf(d) > 0x1.0p24F) {
return swift_format_exponential(dest, length, signbit(d),
digits, digitCount, decimalExponent);
} else {
Expand Down Expand Up @@ -1117,7 +1119,9 @@ size_t swift_format_double(double d, char *dest, size_t length)
int8_t digits[17];
int digitCount =
swift_decompose_double(d, digits, sizeof(digits), &decimalExponent);
if (decimalExponent < -3 || decimalExponent > 15) {
// People use double to model integers <= 2^53, so we use that
// as a cutoff for decimal vs. exponential format.
if (decimalExponent < -3 || fabs(d) > 0x1.0p53) {
return swift_format_exponential(dest, length, signbit(d),
digits, digitCount, decimalExponent);
} else {
Expand Down Expand Up @@ -1166,13 +1170,16 @@ size_t swift_format_float80(long double d, char *dest, size_t length)
}
}

// Decimal numeric formatting
// Decimal numeric formatting
int decimalExponent;
int8_t digits[21];
int digitCount =
swift_decompose_float80(d, digits, sizeof(digits), &decimalExponent);
if (decimalExponent < -3 || decimalExponent > 18) {
// People use long double to model integers <= 2^64, so we use that
// as a cutoff for decimal vs. exponential format.
// The constant is written out as a float80 (aka "long double") literal
// here since it can't be expressed as a 64-bit integer.
if (decimalExponent < -3 || fabsl(d) > 0x1.0p64L) {
return swift_format_exponential(dest, length, signbit(d),
digits, digitCount, decimalExponent);
} else {
Expand Down
48 changes: 34 additions & 14 deletions test/stdlib/PrintFloat.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ PrintTests.test("Printable_Float") {
// Every power of 10 should print with only a single digit '1'
for power in -45 ... 38 {
let s: String
if power < -4 || power > 5 { // Exponential form
if power < -4 || power > 7 { // Exponential form
s = exponentialPowerOfTen(power)
} else if power < 0 { // Fractional decimal form
s = "0." + String(repeating: "0", count: -power - 1) + "1"
Expand All @@ -562,8 +562,14 @@ PrintTests.test("Printable_Float") {
expectAccurateDescription(f.nextUp)
}

// Check that the formatter chooses exponential
// format when it should:
// Float can represent all integers -(2^24)...(2^24)
let maxDecimalForm = Float(1 << 24)
expectDescription("16777216.0", maxDecimalForm)
expectDescription("-16777216.0", -maxDecimalForm)
// Outside of that range, use exponential form
expectDescription("1.6777218e+07", maxDecimalForm.nextUp)
expectDescription("-1.6777218e+07", -maxDecimalForm.nextUp)

expectDescription("1.00001", asFloat32(1.00001))
expectDescription("1.25e+17", asFloat32(125000000000000000.0))
expectDescription("1.25e+16", asFloat32(12500000000000000.0))
Expand All @@ -575,8 +581,8 @@ PrintTests.test("Printable_Float") {
expectDescription("1.25e+10", asFloat32(12500000000.0))
expectDescription("1.25e+09", asFloat32(1250000000.0))
expectDescription("1.25e+08", asFloat32(125000000.0))
expectDescription("1.25e+07", asFloat32(12500000.0))
expectDescription("1.25e+06", asFloat32(1250000.0))
expectDescription("12500000.0", asFloat32(12500000.0))
expectDescription("1250000.0", asFloat32(1250000.0))
expectDescription("125000.0", asFloat32(125000.0))
expectDescription("12500.0", asFloat32(12500.0))
expectDescription("1250.0", asFloat32(1250.0))
Expand Down Expand Up @@ -660,7 +666,7 @@ PrintTests.test("Printable_Double") {
// We know how every power of 10 should print
for power in -323 ... 308 {
let s: String
if power < -4 || power > 14 { // Exponential form
if power < -4 || power > 15 { // Exponential form
s = exponentialPowerOfTen(power)
} else if power < 0 { // Fractional decimal form
s = "0." + String(repeating: "0", count: -power - 1) + "1"
Expand All @@ -687,12 +693,18 @@ PrintTests.test("Printable_Double") {
}
}

// Check that the formatter chooses exponential
// format when it should:
// Double can represent all integers -(2^53)...(2^53)
let maxDecimalForm = Double((1 as Int64) << 53)
expectDescription("9007199254740992.0", maxDecimalForm)
expectDescription("-9007199254740992.0", -maxDecimalForm)
// Outside of that range, we use exponential form:
expectDescription("9.007199254740994e+15", maxDecimalForm.nextUp)
expectDescription("-9.007199254740994e+15", -maxDecimalForm.nextUp)

expectDescription("1.00000000000001", asFloat64(1.00000000000001))
expectDescription("1.25e+17", asFloat64(125000000000000000.0))
expectDescription("1.25e+16", asFloat64(12500000000000000.0))
expectDescription("1.25e+15", asFloat64(1250000000000000.0))
expectDescription("1250000000000000.0", asFloat64(1250000000000000.0))
expectDescription("125000000000000.0", asFloat64(125000000000000.0))
expectDescription("12500000000000.0", asFloat64(12500000000000.0))
expectDescription("1250000000000.0", asFloat64(1250000000000.0))
Expand Down Expand Up @@ -769,7 +781,7 @@ PrintTests.test("Printable_Float80") {
// We know how every power of 10 should print
for power in -4950 ... 4932 {
let s: String
if power < -4 || power > 17 { // Exponential form
if power < -4 || power > 19 { // Exponential form
s = exponentialPowerOfTen(power)
} else if power < 0 { // Fractional decimal form
s = "0." + String(repeating: "0", count: -power - 1) + "1"
Expand All @@ -794,11 +806,19 @@ PrintTests.test("Printable_Float80") {
}
}

// Check that the formatter chooses exponential
// format when it should:
// Float80 can represent all integers -(2^64)...(2^64):
let maxDecimalForm = Float80(UInt64.max) + 1.0
expectDescription("18446744073709551616.0", maxDecimalForm)
expectDescription("-18446744073709551616.0", -maxDecimalForm)
// Outside of that range, use exponential form
expectDescription("1.8446744073709551618e+19", maxDecimalForm.nextUp)
expectDescription("-1.8446744073709551618e+19", -maxDecimalForm.nextUp)

expectDescription("1.00000000000000001", asFloat80(1.00000000000000001))
expectDescription("1.25e+19", asFloat80(12500000000000000000.0))
expectDescription("1.25e+18", asFloat80(1250000000000000000.0))
expectDescription("1.25e+21", asFloat80(1250000000000000000000.0))
expectDescription("1.25e+20", asFloat80(125000000000000000000.0))
expectDescription("12500000000000000000.0", asFloat80(12500000000000000000.0))
expectDescription("1250000000000000000.0", asFloat80(1250000000000000000.0))
expectDescription("125000000000000000.0", asFloat80(125000000000000000.0))
expectDescription("12500000000000000.0", asFloat80(12500000000000000.0))
expectDescription("1250000000000000.0", asFloat80(1250000000000000.0))
Expand Down