| Differences between
and this patch
- Source/JavaScriptCore/ChangeLog +41 lines
Lines 1-3 Source/JavaScriptCore/ChangeLog_sec1
1
2011-11-19  Nikolas Zimmermann  <nzimmermann@rim.com>
2
3
        Add flags/precision arguments to String::number(double) to allow fine-grained control over the result string
4
        https://bugs.webkit.org/show_bug.cgi?id=72793
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        This new code will be used in follow-up patches to replace the String::format("%.2f") usage in
9
        platform/text/TextStream.cpp, and String::format("%.6lg") usage in svg/SVGPathStringBuilder.cpp.
10
11
        The String::number(double) currently calls String::format("%.6lg") in trunk. In order to replace
12
        this by a variant that properly rounds to six significant figures, JSC code could be refactored.
13
        JSCs Number.toPrecision/toFixed uses wtf/dtoa/double-conversion which provides all features we need,
14
        except truncating trailing zeros, needed to mimic the "g" format, which is either f or e but with
15
        trailing zeros removed, producing shorter results. Changed the default signature to:
16
17
        "static String number(double, unsigned = ShouldRoundSignificantFigures | ShouldTruncateTrailingZeros, unsigned precision = 6);".
18
19
        In WebCore we can now replace String::format() calls like this:
20
        String::format("%.2f", f) -> String::number(f, ShouldRoundDecimalPlaces, 2)
21
        String::format("%.6lg", f) -> String::number(f)
22
23
        The default parameters for precison & flags exactly match the format of the string produced now, except that the result
24
        is rounded according to the rounding mode / formatting mode and precision. This paves the way towards reliable results
25
        in the d="" attribute dumps of SVG paths  across platforms. The dtoa rounding code enforces a unique zero, resolving
26
        all 0.0 vs. -0.0 issues currently seen on Windows, and some Gtk/Qt bots.
27
28
        * JavaScriptCore.exp: Change String::number(double) signature.
29
        * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: Ditto.
30
        * runtime/NumberPrototype.cpp:
31
        (JSC::numberProtoFuncToFixed): Refactor this into numberToFixedPrecisionString(), move to wtf/dtoa.cpp.
32
        (JSC::numberProtoFuncToPrecision): Ditto, refactor this into numberToFixedWidthString.
33
        * wtf/dtoa.cpp: Moved fixedWidth/Precision helpers into dtoa, extend numberToFixedPrecisionString(). Add a mode which allows to truncate trailing zeros/decimal point.
34
                        to make it possible to use them to generate strings that match the output from String::format("%6.lg"), while using our dtoas rounding facilities.
35
        * wtf/dtoa.h:
36
        * wtf/dtoa/utils.h: Expose new helper method, which allows us to truncate the result, before generating the output const char*.
37
        (WTF::double_conversion::StringBuilder::SetPosition):
38
        * wtf/text/WTFString.cpp:
39
        (WTF::String::number): Remove String::format("%6.lg") usage! Switch to rounding to six significant figures, while matching the output of String::format.
40
        * wtf/text/WTFString.h:
41
1
2011-11-18  Daniel Bates  <dbates@rim.com>
42
2011-11-18  Daniel Bates  <dbates@rim.com>
2
43
3
        Add CMake build infrastructure for the BlackBerry port
44
        Add CMake build infrastructure for the BlackBerry port
- Source/JavaScriptCore/JavaScriptCore.exp -1 / +3 lines
Lines 477-482 __ZN3WTF4SHA111computeHashERNS_6VectorIh Source/JavaScriptCore/JavaScriptCore.exp_sec1
477
__ZN3WTF4SHA18addBytesEPKhm
477
__ZN3WTF4SHA18addBytesEPKhm
478
__ZN3WTF4SHA1C1Ev
478
__ZN3WTF4SHA1C1Ev
479
__ZN3WTF4dtoaEPcdRbRiRj
479
__ZN3WTF4dtoaEPcdRbRiRj
480
__ZN3WTF11dtoaRoundDPEPcdiRbRiRj
481
__ZN3WTF11dtoaRoundSFEPcdiRbRiRj
480
__ZN3WTF5Mutex4lockEv
482
__ZN3WTF5Mutex4lockEv
481
__ZN3WTF5Mutex6unlockEv
483
__ZN3WTF5Mutex6unlockEv
482
__ZN3WTF5Mutex7tryLockEv
484
__ZN3WTF5Mutex7tryLockEv
Lines 495-501 __ZN3WTF6String6appendERKS0_ Source/JavaScriptCore/JavaScriptCore.exp_sec2
495
__ZN3WTF6String6appendEt
497
__ZN3WTF6String6appendEt
496
__ZN3WTF6String6formatEPKcz
498
__ZN3WTF6String6formatEPKcz
497
__ZN3WTF6String6insertERKS0_j
499
__ZN3WTF6String6insertERKS0_j
498
__ZN3WTF6String6numberEd
500
__ZN3WTF6String6numberEdjj
499
__ZN3WTF6String6numberEi
501
__ZN3WTF6String6numberEi
500
__ZN3WTF6String6numberEj
502
__ZN3WTF6String6numberEj
501
__ZN3WTF6String6numberEl
503
__ZN3WTF6String6numberEl
- Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def +3 lines
Lines 248-257 EXPORTS Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def_sec1
248
    ?name@JSFunction@JSC@@QAEABVUString@2@PAVExecState@2@@Z
248
    ?name@JSFunction@JSC@@QAEABVUString@2@PAVExecState@2@@Z
249
    ?newUninitialized@CString@WTF@@SA?AV12@IAAPAD@Z
249
    ?newUninitialized@CString@WTF@@SA?AV12@IAAPAD@Z
250
    ?nullptr@@3Vnullptr_t@std@@A
250
    ?nullptr@@3Vnullptr_t@std@@A
251
    ?number@String@WTF@@SA?AV12@NII@Z
251
    ?number@UString@JSC@@SA?AV12@H@Z
252
    ?number@UString@JSC@@SA?AV12@H@Z
252
    ?number@UString@JSC@@SA?AV12@I@Z
253
    ?number@UString@JSC@@SA?AV12@I@Z
253
    ?number@UString@JSC@@SA?AV12@N@Z
254
    ?number@UString@JSC@@SA?AV12@N@Z
254
    ?numberToString@WTF@@YAPBDNQAD@Z
255
    ?numberToString@WTF@@YAPBDNQAD@Z
256
    ?numberToFixedPrecisionString@WTF@@YAPBDNIQAD_N@Z
257
    ?numberToFixedWidthString@WTF@@YAPBDNIQAD@Z
255
    ?objectCount@Heap@JSC@@QAEIXZ
258
    ?objectCount@Heap@JSC@@QAEIXZ
256
    ?objectProtoFuncToString@JSC@@YI_JPAVExecState@1@@Z
259
    ?objectProtoFuncToString@JSC@@YI_JPAVExecState@1@@Z
257
    ?objectTypeCounts@Heap@JSC@@QAE?AV?$PassOwnPtr@V?$HashCountedSet@PBDU?$PtrHash@PBD@WTF@@U?$HashTraits@PBD@2@@WTF@@@WTF@@XZ
260
    ?objectTypeCounts@Heap@JSC@@QAE?AV?$PassOwnPtr@V?$HashCountedSet@PBDU?$PtrHash@PBD@WTF@@U?$HashTraits@PBD@2@@WTF@@@WTF@@XZ
- Source/JavaScriptCore/runtime/NumberPrototype.cpp -10 / +2 lines
Lines 396-406 EncodedJSValue JSC_HOST_CALL numberProto Source/JavaScriptCore/runtime/NumberPrototype.cpp_sec1
396
    ASSERT(isfinite(x));
396
    ASSERT(isfinite(x));
397
397
398
    char buffer[WTF::NumberToStringBufferLength];
398
    char buffer[WTF::NumberToStringBufferLength];
399
    DoubleConversionStringBuilder builder(buffer, WTF::NumberToStringBufferLength);
399
    return JSValue::encode(jsString(exec, UString(numberToFixedWidthString(x, decimalPlaces, buffer))));
400
    const DoubleToStringConverter& converter = DoubleToStringConverter::EcmaScriptConverter();
401
    builder.Reset();
402
    converter.ToFixed(x, decimalPlaces, &builder);
403
    return JSValue::encode(jsString(exec, UString(builder.Finalize())));
404
}
400
}
405
401
406
// toPrecision converts a number to a string, takeing an argument specifying a
402
// toPrecision converts a number to a string, takeing an argument specifying a
Lines 431-441 EncodedJSValue JSC_HOST_CALL numberProto Source/JavaScriptCore/runtime/NumberPrototype.cpp_sec2
431
        return JSValue::encode(jsString(exec, UString::number(x)));
427
        return JSValue::encode(jsString(exec, UString::number(x)));
432
428
433
    char buffer[WTF::NumberToStringBufferLength];
429
    char buffer[WTF::NumberToStringBufferLength];
434
    DoubleConversionStringBuilder builder(buffer, WTF::NumberToStringBufferLength);
430
    return JSValue::encode(jsString(exec, UString(numberToFixedPrecisionString(x, significantFigures, buffer))));
435
    const DoubleToStringConverter& converter = DoubleToStringConverter::EcmaScriptConverter();
436
    builder.Reset();
437
    converter.ToPrecision(x, significantFigures, &builder);
438
    return JSValue::encode(jsString(exec, UString(builder.Finalize())));
439
}
431
}
440
432
441
EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec)
433
EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec)
- Source/JavaScriptCore/wtf/dtoa.cpp -2 / +65 lines
Lines 1792-1799 void dtoaRoundDP(DtoaBuffer result, doub Source/JavaScriptCore/wtf/dtoa.cpp_sec1
1792
    dtoa<false, false, true, false>(result, dd, ndigits, sign, exponent, precision);
1792
    dtoa<false, false, true, false>(result, dd, ndigits, sign, exponent, precision);
1793
}
1793
}
1794
1794
1795
1795
const char* numberToString(double d, NumberToStringBuffer buffer)
1796
const char *numberToString(double d, NumberToStringBuffer buffer)
1797
{
1796
{
1798
    double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength);
1797
    double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength);
1799
    const double_conversion::DoubleToStringConverter& converter = double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1798
    const double_conversion::DoubleToStringConverter& converter = double_conversion::DoubleToStringConverter::EcmaScriptConverter();
Lines 1801-1804 const char *numberToString(double d, Num Source/JavaScriptCore/wtf/dtoa.cpp_sec2
1801
    return builder.Finalize();
1800
    return builder.Finalize();
1802
}
1801
}
1803
1802
1803
static inline const char* formatStringTruncatingTrailingZerosIfNeeded(NumberToStringBuffer buffer, double_conversion::StringBuilder& builder)
1804
{
1805
    size_t length = builder.position();
1806
    size_t decimalPointPosition = 0;
1807
    for (; decimalPointPosition < length; ++decimalPointPosition) {
1808
        if (buffer[decimalPointPosition] == '.')
1809
            break;
1810
    }
1811
1812
    // No decimal seperator found, early exit.
1813
    if (decimalPointPosition == length)
1814
        return builder.Finalize();
1815
1816
    size_t truncatedLength = length - 1;
1817
    for (; truncatedLength > decimalPointPosition; --truncatedLength) {
1818
        if (buffer[truncatedLength] != '0')
1819
            break;
1820
    }
1821
1822
    // No trailing zeros found to strip.
1823
    if (truncatedLength == length - 1)
1824
        return builder.Finalize();
1825
1826
    // If we removed all trailing zeros, remove the decimal point as well.
1827
    if (truncatedLength == decimalPointPosition) {
1828
        ASSERT(truncatedLength > 0);
1829
        --truncatedLength;
1830
    }
1831
1832
    // Truncate the StringBuilder, and return the final result.
1833
    builder.SetPosition(truncatedLength + 1);
1834
    return builder.Finalize();
1835
}
1836
1837
const char* numberToFixedPrecisionString(double d, unsigned significantFigures, NumberToStringBuffer buffer, bool truncateTrailingZeros)
1838
{
1839
    // Mimic String::format("%.[precision]g", ...), but use dtoas rounding facilities.
1840
    // "g": Signed value printed in f or e format, whichever is more compact for the given value and precision.
1841
    // The e format is used only when the exponent of the value is less than –4 or greater than or equal to the
1842
    // precision argument. Trailing zeros are truncated, and the decimal point appears only if one or more digits follow it.
1843
    // "precision": The precision specifies the maximum number of significant digits printed.
1844
    double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength);
1845
    const double_conversion::DoubleToStringConverter& converter = double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1846
    converter.ToPrecision(d, significantFigures, &builder);
1847
    if (!truncateTrailingZeros)
1848
        return builder.Finalize();
1849
    return formatStringTruncatingTrailingZerosIfNeeded(buffer, builder);
1850
}
1851
1852
const char* numberToFixedWidthString(double d, unsigned decimalPlaces, NumberToStringBuffer buffer)
1853
{
1854
    // Mimic String::format("%.[precision]f", ...), but use dtoas rounding facilities.
1855
    // "f": Signed value having the form [ – ]dddd.dddd, where dddd is one or more decimal digits.
1856
    // The number of digits before the decimal point depends on the magnitude of the number, and
1857
    // the number of digits after the decimal point depends on the requested precision.
1858
    // "precision": The precision value specifies the number of digits after the decimal point.
1859
    // If a decimal point appears, at least one digit appears before it.
1860
    // The value is rounded to the appropriate number of digits.    
1861
    double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength);
1862
    const double_conversion::DoubleToStringConverter& converter = double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1863
    converter.ToFixed(d, decimalPlaces, &builder);
1864
    return builder.Finalize();
1865
}
1866
1804
} // namespace WTF
1867
} // namespace WTF
- Source/JavaScriptCore/wtf/dtoa.h -1 / +5 lines
Lines 43-54 double strtod(const char* s00, char** se Source/JavaScriptCore/wtf/dtoa.h_sec1
43
const unsigned NumberToStringBufferLength = 96;
43
const unsigned NumberToStringBufferLength = 96;
44
typedef char NumberToStringBuffer[NumberToStringBufferLength];
44
typedef char NumberToStringBuffer[NumberToStringBufferLength];
45
typedef UChar NumberToUStringBuffer[NumberToStringBufferLength];
45
typedef UChar NumberToUStringBuffer[NumberToStringBufferLength];
46
const char *numberToString(double, NumberToStringBuffer);
46
const char* numberToString(double, NumberToStringBuffer);
47
const char* numberToFixedPrecisionString(double, unsigned significantFigures, NumberToStringBuffer, bool truncateTrailingZeros = false);
48
const char* numberToFixedWidthString(double, unsigned decimalPlaces, NumberToStringBuffer);
47
49
48
} // namespace WTF
50
} // namespace WTF
49
51
50
using WTF::NumberToStringBuffer;
52
using WTF::NumberToStringBuffer;
51
using WTF::NumberToUStringBuffer;
53
using WTF::NumberToUStringBuffer;
52
using WTF::numberToString;
54
using WTF::numberToString;
55
using WTF::numberToFixedPrecisionString;
56
using WTF::numberToFixedWidthString;
53
57
54
#endif // WTF_dtoa_h
58
#endif // WTF_dtoa_h
- Source/JavaScriptCore/wtf/dtoa/utils.h +8 lines
Lines 197-202 namespace double_conversion { Source/JavaScriptCore/wtf/dtoa/utils.h_sec1
197
            ASSERT(!is_finalized());
197
            ASSERT(!is_finalized());
198
            return position_;
198
            return position_;
199
        }
199
        }
200
         
201
        // Set the current position in the builder.
202
        void SetPosition(int position)
203
        {
204
            ASSERT(!is_finalized());
205
            ASSERT(position < size());
206
            position_ = position;
207
        }
200
        
208
        
201
        // Reset the position.
209
        // Reset the position.
202
        void Reset() { position_ = 0; }
210
        void Reset() { position_ = 0; }
- Source/JavaScriptCore/wtf/text/WTFString.cpp -2 / +9 lines
Lines 458-466 String String::number(unsigned long long Source/JavaScriptCore/wtf/text/WTFString.cpp_sec1
458
#endif
458
#endif
459
}
459
}
460
    
460
    
461
String String::number(double n)
461
String String::number(double number, unsigned flags, unsigned precision)
462
{
462
{
463
    return String::format("%.6lg", n);
463
    char buffer[WTF::NumberToStringBufferLength];
464
465
    // Mimic String::format("%.[precision]g", ...), but use dtoas rounding facilities.
466
    if (flags & ShouldRoundSignificantFigures)
467
        return String(numberToFixedPrecisionString(number, precision, buffer, flags & ShouldTruncateTrailingZeros));
468
469
    // Mimic String::format("%.[precision]f", ...), but use dtoas rounding facilities.
470
    return String(numberToFixedWidthString(number, precision, buffer));
464
}
471
}
465
472
466
int String::toIntStrict(bool* ok, int base) const
473
int String::toIntStrict(bool* ok, int base) const
- Source/JavaScriptCore/wtf/text/WTFString.h -1 / +8 lines
Lines 82-87 WTF_EXPORT_PRIVATE double charactersToDo Source/JavaScriptCore/wtf/text/WTFString.h_sec1
82
float charactersToFloat(const LChar*, size_t, bool* ok = 0, bool* didReadNumber = 0);
82
float charactersToFloat(const LChar*, size_t, bool* ok = 0, bool* didReadNumber = 0);
83
float charactersToFloat(const UChar*, size_t, bool* ok = 0, bool* didReadNumber = 0);
83
float charactersToFloat(const UChar*, size_t, bool* ok = 0, bool* didReadNumber = 0);
84
84
85
enum FloatConversionFlags {
86
    ShouldRoundSignificantFigures = 1 << 0,
87
    ShouldRoundDecimalPlaces = 1 << 1,
88
    ShouldTruncateTrailingZeros = 1 << 2
89
};
90
85
template<bool isSpecialCharacter(UChar)> bool isAllSpecialCharacters(const UChar*, size_t);
91
template<bool isSpecialCharacter(UChar)> bool isAllSpecialCharacters(const UChar*, size_t);
86
92
87
class String {
93
class String {
Lines 182-188 public: Source/JavaScriptCore/wtf/text/WTFString.h_sec2
182
    WTF_EXPORT_PRIVATE static String number(unsigned long);
188
    WTF_EXPORT_PRIVATE static String number(unsigned long);
183
    WTF_EXPORT_PRIVATE static String number(long long);
189
    WTF_EXPORT_PRIVATE static String number(long long);
184
    WTF_EXPORT_PRIVATE static String number(unsigned long long);
190
    WTF_EXPORT_PRIVATE static String number(unsigned long long);
185
    WTF_EXPORT_PRIVATE static String number(double);
191
    WTF_EXPORT_PRIVATE static String number(double, unsigned = ShouldRoundSignificantFigures | ShouldTruncateTrailingZeros, unsigned precision = 6);
186
192
187
    // Find a single character or string, also with match function & latin1 forms.
193
    // Find a single character or string, also with match function & latin1 forms.
188
    size_t find(UChar c, unsigned start = 0) const
194
    size_t find(UChar c, unsigned start = 0) const
Lines 601-606 using WTF::find; Source/JavaScriptCore/wtf/text/WTFString.h_sec3
601
using WTF::isAllSpecialCharacters;
607
using WTF::isAllSpecialCharacters;
602
using WTF::isSpaceOrNewline;
608
using WTF::isSpaceOrNewline;
603
using WTF::reverseFind;
609
using WTF::reverseFind;
610
using WTF::ShouldRoundDecimalPlaces;
604
611
605
#include "AtomicString.h"
612
#include "AtomicString.h"
606
#endif
613
#endif

Return to Bug 72793