mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #85797 from tbkka/tbkka-float-parsing
Reimplement Floating-Point Parsing in pure Swift
This commit is contained in:
@@ -80,6 +80,7 @@ add_library(swiftCore
|
||||
FlatMap.swift
|
||||
Flatten.swift
|
||||
FloatingPoint.swift
|
||||
FloatingPointFromString.swift
|
||||
FloatingPointToString.swift
|
||||
Hashable.swift
|
||||
AnyHashable.swift # ORDER DEPENDENCY
|
||||
|
||||
@@ -32,6 +32,15 @@ extern "C" {
|
||||
SWIFT_RUNTIME_STDLIB_API
|
||||
void *_swift_objCMirrorSummary(const void * nsObject);
|
||||
|
||||
// Caveat: _swift_stdlib_strto{f,d,ld}_clocale() was called directly from inlineable
|
||||
// code until Feb 2020 (commit 4d0e2adbef4d changed this), so they need to be
|
||||
// exported with the same C-callable ABI for as long as we support code compiled
|
||||
// with Swift 5.3.
|
||||
|
||||
// _swift_stdlib_strto{d,f,f16}_clocale were reimplemented in
|
||||
// Swift in Dec 2025. See FloatingPointFromString.swift
|
||||
// _swift_stdlib_strtold_clocale was not reimplemented at that time.
|
||||
|
||||
/// Call strtold_l with the C locale, swapping argument and return
|
||||
/// types so we can operate on Float80.
|
||||
SWIFT_RUNTIME_STDLIB_API
|
||||
@@ -46,6 +55,8 @@ SWIFT_RUNTIME_STDLIB_API
|
||||
const char *_swift_stdlib_strtof_clocale(const char *nptr, float *outResult);
|
||||
/// Call strtof_l with the C locale, swapping argument and return
|
||||
/// types so we can operate consistently on Float80.
|
||||
// FIXME? Can this be deleted? _swift_stdlib_strtof16_clocale was never
|
||||
// actually called from inlineable code.
|
||||
SWIFT_RUNTIME_STDLIB_API
|
||||
const char *_swift_stdlib_strtof16_clocale(const char *nptr, __fp16 *outResult);
|
||||
|
||||
|
||||
@@ -261,6 +261,7 @@ split_embedded_sources(
|
||||
OUT_LIST_NORMAL SWIFTLIB_GYB_SOURCES
|
||||
|
||||
NORMAL AtomicInt.swift.gyb
|
||||
EMBEDDED FloatingPointFromString.swift
|
||||
EMBEDDED FloatingPointParsing.swift.gyb
|
||||
EMBEDDED FloatingPointToString.swift
|
||||
EMBEDDED FloatingPointTypes.swift.gyb
|
||||
|
||||
2520
stdlib/public/core/FloatingPointFromString.swift
Normal file
2520
stdlib/public/core/FloatingPointFromString.swift
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,13 @@
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This defines the public APIs for parsing floating point numbers.
|
||||
//
|
||||
// The backing implementation is in `FloatingPointFromString.swift`
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
import SwiftShims
|
||||
|
||||
@@ -122,6 +129,10 @@ extension ${Self}: LosslessStringConvertible {
|
||||
/// // p?.isNaN == true
|
||||
/// // String(p!) == "nan(0x10)"
|
||||
///
|
||||
/// - An input string of `"snan"` (case insensitive) is converted
|
||||
/// into a *signaling NaN* value. This form permits an optional
|
||||
/// payload in the same format as for a non-signaling NaN.
|
||||
///
|
||||
/// A string in any other format than those described above
|
||||
/// or containing additional characters
|
||||
/// results in a `nil` value. For example, the following conversions
|
||||
@@ -134,7 +145,7 @@ extension ${Self}: LosslessStringConvertible {
|
||||
/// A decimal or hexadecimal string is converted to a `${Self}`
|
||||
/// instance using the IEEE 754 roundTiesToEven (default) rounding
|
||||
/// attribute.
|
||||
/// Values with absolute value smaller than `${Self}.leastNonzeroMagnitude`
|
||||
/// Values with absolute value smaller than one-half of `${Self}.leastNonzeroMagnitude`
|
||||
/// are rounded to plus or minus zero.
|
||||
/// Values with absolute value larger than `${Self}.greatestFiniteMagnitude`
|
||||
/// are rounded to plus or minus infinity.
|
||||
@@ -161,13 +172,26 @@ extension ${Self}: LosslessStringConvertible {
|
||||
/// - Parameter text: An input string to convert to a `${Self}?` instance.
|
||||
///
|
||||
@inlinable
|
||||
// This is inlinable because the unspecialized generic takes a protocol
|
||||
// argument; inlining might be able to specialize away the existential.
|
||||
public init?<S: StringProtocol>(_ text: S) {
|
||||
%if bits == 16:
|
||||
// Float16 has only been supported since Swift 5.3, so it can
|
||||
// unconditionally delegate to init(_:Substring)
|
||||
self.init(Substring(text))
|
||||
%else:
|
||||
if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { //SwiftStdlib 5.3
|
||||
if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
|
||||
// On SwiftStdlib 5.3 and later, we can just delegate to the
|
||||
// init(_:Substring) initializer:
|
||||
self.init(Substring(text))
|
||||
} else {
|
||||
// When running on SwiftStdlib <5.3, we can't just delegate to
|
||||
// the init(_:Substring) initializer, so we have to inline the whole thing...
|
||||
|
||||
// This means we must preserve the _swift_stdlib_strto{f,d,ld}_clocale
|
||||
// ABI for as long as we support code compiled for Swift < 5.3, which
|
||||
// includes macOS < 11.0, iOS/tvOS < 14.0, or watchOS < 7.0
|
||||
|
||||
self = 0.0
|
||||
let success = unsafe _withUnprotectedUnsafeMutablePointer(to: &self) { p -> Bool in
|
||||
unsafe text.withCString { chars -> Bool in
|
||||
@@ -191,14 +215,77 @@ extension ${Self}: LosslessStringConvertible {
|
||||
%end
|
||||
}
|
||||
|
||||
// Caveat: This implementation used to be inlineable.
|
||||
// In particular, we still have to export
|
||||
// _swift_stdlib_strtoXYZ_clocale()
|
||||
// as ABI to support old compiled code that still requires it.
|
||||
% if bits in [16, 32, 64]:
|
||||
|
||||
// Various entry points that take concrete types. Note that these
|
||||
// are deliberately NOT inlinable:
|
||||
// * There is no performance advantage to inlining
|
||||
// * There is a code size disadvantage to inlining
|
||||
// * We don't want to use the `StringProtocol` API for these common cases as
|
||||
// that pessimizes either performance or code size depending on whether it
|
||||
// gets inlined.
|
||||
|
||||
// TODO: Expose a concrete `String` API to optimize the common case
|
||||
/*
|
||||
@available(SwiftStdlib 6.4, *)
|
||||
public init?(_ text: String) {
|
||||
if let d = parse_float(text.utf8.span) {
|
||||
self = d
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// TODO: Expose Span-taking API publicly
|
||||
// TODO: Some applications would like to restrict the patterns supported
|
||||
// here. We could do that by adding an option set here that indicates
|
||||
// the supported patterns, or we could expose additional entry points to
|
||||
// support common use cases (e.g., JSON).
|
||||
/*
|
||||
@available(SwiftStdlib 6.4, *)
|
||||
public init?(_ text: Span<UInt8>) {
|
||||
if let d = parse_float(text) {
|
||||
self = d
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Use the all-Swift `parse_float${bits}()` implementation for Float16/32/64
|
||||
@available(SwiftStdlib 5.3, *)
|
||||
public init?(_ text: Substring) {
|
||||
// TODO: Someday, this whole function should simplify down to just:
|
||||
// ${Self}(text.utf8.span)
|
||||
#if _pointerBitWidth(_16)
|
||||
// Always fail on 16-bit targets
|
||||
return nil
|
||||
#else
|
||||
// Work around span availability limits
|
||||
let parsed = unsafe text.base._guts.withFastUTF8 { chars -> ${Self}? in
|
||||
unsafe parse_float${bits}(chars.span)
|
||||
}
|
||||
|
||||
if let parsed {
|
||||
self = parsed
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
% elif bits in [80]:
|
||||
|
||||
// For now, continue relying on libc-based implementation for Float80 support
|
||||
// TODO: Implement `parse_float80(_:Span)` so this can
|
||||
// be implemented the same as above.
|
||||
@available(SwiftStdlib 5.3, *)
|
||||
public init?(_ text: Substring) {
|
||||
self = 0.0
|
||||
let success = unsafe _withUnprotectedUnsafeMutablePointer(to: &self) { p -> Bool in
|
||||
// Make a copy of `text` to ensure we have a null-terminated C string
|
||||
// to pass to the libc API:
|
||||
unsafe text.withCString { chars -> Bool in
|
||||
switch unsafe chars[0] {
|
||||
case 9, 10, 11, 12, 13, 32:
|
||||
@@ -217,8 +304,10 @@ extension ${Self}: LosslessStringConvertible {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
% end
|
||||
}
|
||||
|
||||
|
||||
% if bits in [16,80]:
|
||||
#endif
|
||||
% end
|
||||
|
||||
@@ -178,6 +178,7 @@
|
||||
"UInt128.swift"],
|
||||
"Floating": [
|
||||
"FloatingPoint.swift",
|
||||
"FloatingPointFromString.swift",
|
||||
"FloatingPointParsing.swift",
|
||||
"FloatingPointToString.swift",
|
||||
"FloatingPointTypes.swift",
|
||||
|
||||
@@ -329,30 +329,9 @@ const char *_swift_stdlib_strtold_clocale(const char *nptr, void *outResult) {
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *_swift_stdlib_strtod_clocale(const char *nptr, double *outResult) {
|
||||
#if SWIFT_STDLIB_HAS_LOCALE
|
||||
return _swift_stdlib_strtoX_clocale_impl(nptr, outResult, HUGE_VAL, strtod_l);
|
||||
#else
|
||||
return _swift_stdlib_strtoX_impl(nptr, outResult, strtod);
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *_swift_stdlib_strtof_clocale(const char *nptr, float *outResult) {
|
||||
#if SWIFT_STDLIB_HAS_LOCALE
|
||||
return _swift_stdlib_strtoX_clocale_impl(nptr, outResult, HUGE_VALF,
|
||||
strtof_l);
|
||||
#else
|
||||
return _swift_stdlib_strtoX_impl(nptr, outResult, strtof);
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *_swift_stdlib_strtof16_clocale(const char *nptr,
|
||||
__fp16 *outResult) {
|
||||
float tmp;
|
||||
const char *result = _swift_stdlib_strtof_clocale(nptr, &tmp);
|
||||
*outResult = tmp;
|
||||
return result;
|
||||
}
|
||||
// _swift_stdlib_strto{d,f,f16}_clocale were reimplemented in Swift
|
||||
// in December 2025. See FloatingPointFromString.swift.
|
||||
// _swift_stdlib_strtold_clocale was not reimplemented in Swift at that time.
|
||||
|
||||
void _swift_stdlib_flockfile_stdout() {
|
||||
#if defined(_WIN32)
|
||||
|
||||
@@ -13412,7 +13412,6 @@ __swift_stdlib_reportFatalErrorInFile
|
||||
__swift_stdlib_reportUnimplementedInitializer
|
||||
__swift_stdlib_reportUnimplementedInitializerInFile
|
||||
__swift_stdlib_strtod_clocale
|
||||
__swift_stdlib_strtof16_clocale
|
||||
__swift_stdlib_strtof_clocale
|
||||
__swift_stdlib_strtold_clocale
|
||||
__swift_tryRetain
|
||||
|
||||
@@ -13507,7 +13507,6 @@ __swift_stdlib_reportFatalErrorInFile
|
||||
__swift_stdlib_reportUnimplementedInitializer
|
||||
__swift_stdlib_reportUnimplementedInitializerInFile
|
||||
__swift_stdlib_strtod_clocale
|
||||
__swift_stdlib_strtof16_clocale
|
||||
__swift_stdlib_strtof_clocale
|
||||
__swift_stdlib_strtold_clocale
|
||||
__swift_tryRetain
|
||||
|
||||
273
test/stdlib/ParseFloat16.swift
Normal file
273
test/stdlib/ParseFloat16.swift
Normal file
@@ -0,0 +1,273 @@
|
||||
// RUN: %target-run-simple-swift
|
||||
// REQUIRES: executable_test
|
||||
|
||||
// Float16 is not supported on x86_64 macOS
|
||||
// UNSUPPORTED: CPU=x86_64 && OS=macosx
|
||||
|
||||
// Float16 is only available in watchOS 7.0 or newer
|
||||
// UNSUPPORTED: OS=watchos
|
||||
|
||||
// TODO: Figure out why this test breaks on wasm32
|
||||
// UNSUPPORTED: CPU=wasm32
|
||||
|
||||
// Cannot test with old OS stdlib, because that used libc strtof
|
||||
// for parsing, which results in incorrect results.
|
||||
// UNSUPPORTED: use_os_stdlib
|
||||
|
||||
import StdlibUnittest
|
||||
|
||||
let tests = TestSuite("FloatingPointParsing")
|
||||
|
||||
fileprivate func expectRoundTrip(
|
||||
_ value: Float16,
|
||||
stackTrace: SourceLocStack = SourceLocStack(),
|
||||
showFrame: Bool = true,
|
||||
file: String = #file, line: UInt = #line
|
||||
) {
|
||||
let text = value.debugDescription
|
||||
let roundTrip = Float16(Substring(text))
|
||||
expectNotNil(roundTrip, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
if let roundTrip {
|
||||
expectEqual(roundTrip.bitPattern, value.bitPattern, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func expectParse(
|
||||
_ input: String,
|
||||
_ expected: Float16,
|
||||
stackTrace: SourceLocStack = SourceLocStack(),
|
||||
showFrame: Bool = true,
|
||||
file: String = #file, line: UInt = #line
|
||||
) {
|
||||
let parsed = Float16(Substring(input))
|
||||
expectNotNil(parsed, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
if let parsed {
|
||||
expectEqual(parsed.bitPattern, expected.bitPattern, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
func expectParseFails(
|
||||
_ input: String,
|
||||
stackTrace: SourceLocStack = SourceLocStack(),
|
||||
showFrame: Bool = true,
|
||||
file: String = #file, line: UInt = #line
|
||||
) {
|
||||
let parsed = Float16(Substring(input))
|
||||
expectNil(parsed, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
}
|
||||
|
||||
tests.test("Invalids") {
|
||||
expectParseFails("")
|
||||
expectParseFails("-")
|
||||
expectParseFails("+")
|
||||
expectParseFails("&")
|
||||
expectParseFails("+x")
|
||||
expectParseFails("x")
|
||||
}
|
||||
|
||||
tests.test("Infinities") {
|
||||
expectParse("inf", Float16.infinity)
|
||||
expectParse("+inf", Float16.infinity)
|
||||
expectParse("-inf", -Float16.infinity)
|
||||
expectParse("INF", Float16.infinity)
|
||||
expectParse("InF", Float16.infinity)
|
||||
expectParse("iNf", Float16.infinity)
|
||||
expectParse("infinity", Float16.infinity)
|
||||
expectParse("INFINITY", Float16.infinity)
|
||||
expectParse("+infinity", Float16.infinity)
|
||||
expectParse("-infinity", -Float16.infinity)
|
||||
|
||||
expectParseFails("i")
|
||||
expectParseFails("in")
|
||||
expectParseFails(" inf")
|
||||
expectParseFails("- inf")
|
||||
expectParseFails("--inf")
|
||||
expectParseFails("-+inf")
|
||||
expectParseFails("++inf")
|
||||
expectParseFails("inf ")
|
||||
expectParseFails("inx")
|
||||
expectParseFails("-inx")
|
||||
expectParseFails("infi")
|
||||
expectParseFails("infin")
|
||||
expectParseFails("infini")
|
||||
expectParseFails("infinit")
|
||||
expectParseFails("infinite")
|
||||
expectParseFails("infinityandbeyond")
|
||||
|
||||
expectRoundTrip(Float16.infinity)
|
||||
expectRoundTrip(-Float16.infinity)
|
||||
}
|
||||
|
||||
tests.test("NaNs") {
|
||||
// Note: Previous Swift runtime used libc strtof and then
|
||||
// truncated to Float16, which is why some of these are
|
||||
// wrong when testing previous runtimes.
|
||||
expectRoundTrip(Float16.nan)
|
||||
expectRoundTrip(-Float16.nan)
|
||||
expectRoundTrip(Float16(nan:73, signaling:false))
|
||||
expectRoundTrip(Float16(nan:73, signaling:true))
|
||||
expectParse("nan", Float16.nan)
|
||||
expectParse("NAN", Float16.nan)
|
||||
expectParse("NaN", Float16.nan)
|
||||
expectParse("-nan", -Float16.nan)
|
||||
expectParse("nan()", Float16.nan)
|
||||
expectParse("nan(0)", Float16.nan)
|
||||
expectParse("nan(000000000000000000000000000000000000000)", Float16.nan)
|
||||
expectParse("nan(0x00000000000000000000000000000000000000)", Float16.nan)
|
||||
expectParse("nan(10)", Float16(nan:10, signaling:false))
|
||||
expectParse("nan(0x10)", Float16(nan:16, signaling:false))
|
||||
expectParse("nan(010)", Float16(nan:8, signaling:false))
|
||||
expectParse("nan(9)", Float16(nan:9, signaling:false))
|
||||
expectParse("nan(99)", Float16(nan:99, signaling:false))
|
||||
expectParse("nan(255)", Float16(nan:255, signaling:false))
|
||||
expectParse("nan(256)", Float16(nan:0, signaling:false))
|
||||
expectParse("nan(511)", Float16(nan:255, signaling:false))
|
||||
expectParse("nan(999999)", Float16(nan:63, signaling:false))
|
||||
expectParse("nan(999999999999999)", Float16(nan:255, signaling:false))
|
||||
expectParseFails("n")
|
||||
expectParseFails("na")
|
||||
expectParseFails("nann")
|
||||
expectParseFails("nananananana")
|
||||
}
|
||||
|
||||
tests.test("HexFloats") {
|
||||
expectParseFails("0x")
|
||||
expectParseFails("0x.")
|
||||
expectParseFails("0x😀")
|
||||
expectParseFails("0x1😀p2")
|
||||
expectParseFails("0x1.07😀p2")
|
||||
expectParseFails("0x1p😀")
|
||||
expectParseFails("0x1p+😀")
|
||||
expectParseFails("0x1p")
|
||||
expectParseFails("0x1p+")
|
||||
expectParseFails("0xp+7")
|
||||
expectParseFails("0x.p1")
|
||||
expectParseFails("0x..p1")
|
||||
expectParseFails("0x0p1.0")
|
||||
expectParse("0x0p0", 0.0)
|
||||
expectParse("0x0p1", 0.0)
|
||||
expectParse("-0x0p0", -0.0)
|
||||
expectParse("0x0p999999999", 0.0)
|
||||
expectParse("0x0.0p999999999", 0.0)
|
||||
expectParse("0x.0p-999999999", 0.0)
|
||||
expectParse("0x0p-999999999", 0.0)
|
||||
expectParse("0x1p-25", 0.0)
|
||||
expectParse("0x.000001", Float16.leastNonzeroMagnitude)
|
||||
expectParse("0x1p-24", Float16.leastNonzeroMagnitude)
|
||||
expectParse("0x1p-24", Float16(bitPattern:1))
|
||||
expectParse("0x1p-23", Float16(bitPattern:2))
|
||||
expectParse("0x1p-22", Float16(bitPattern:4))
|
||||
expectParse("0x1p-21", Float16(bitPattern:8))
|
||||
expectParse("0x1p-20", Float16(bitPattern:16))
|
||||
|
||||
// Test the tricky rounding of values between the largest subnormal and least normal
|
||||
expectParse("0x0.ffcp-14", Float16.leastNormalMagnitude.nextDown) // Largest subnormal
|
||||
expectParse("0x0.ffdffffp-14", Float16.leastNormalMagnitude.nextDown) // Just less than halfway
|
||||
expectParse("0x0.ffep-14", Float16.leastNormalMagnitude) // Halfway
|
||||
expectParse("0x0.ffe0000001p-14", Float16.leastNormalMagnitude) // Just above halfway
|
||||
expectParse("0x1p-14", Float16.leastNormalMagnitude) // Smallest normal
|
||||
|
||||
expectParse("0x1.554p-2", 1.0/3.0)
|
||||
expectParse("0x0.ffe", 0.99951172)
|
||||
expectParse("0x0.ffeffff", 0.99951172) // Note: Old Swift implementation gets this wrong
|
||||
expectParse("0x0.fff", 1.0)
|
||||
expectParse("0x1p0", 1.0)
|
||||
expectParse("0x1.002", 1.0) // Exact halfway between 1.0 and succ(1.0), rounds even
|
||||
// Note: Old implementation gets this wrong due to double-rounding
|
||||
expectParse("0x1.0020000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
||||
1.00097656) // Teensy-tiny bit bigger than above, should round up
|
||||
expectParse("0x1.00200000000001", 1.00097656) // Bigger than above
|
||||
expectParse("0x1002000000000000000000001p-96", 1.00097656)
|
||||
expectParse("0x0000000000000.000000000000000000000001002000000000000000000001p96", 1.00097656)
|
||||
expectParse("0x1.004", 1.00097656)
|
||||
expectParse("0x1p+1", 2.0)
|
||||
expectParse("0x1p+0000000000000000000000000000000000001", 2.0)
|
||||
expectParse("0x12", 18.0)
|
||||
expectParse("0xab", 171.0)
|
||||
expectParse("0x1p+10", 1024.0)
|
||||
expectParse("0x1p+0000000000000000000000000010", 1024.0)
|
||||
expectParse("0x1.920p1", Float16.pi)
|
||||
expectParse("0xffe0", Float16.greatestFiniteMagnitude)
|
||||
expectParse("0xffe00000000000.0000000000000000000000000000000000000000001p-40",
|
||||
Float16.greatestFiniteMagnitude)
|
||||
expectParse("0x1.ffcp+15", Float16.greatestFiniteMagnitude)
|
||||
|
||||
expectParse("0xffa0", 65440.0) // Exact (odd)
|
||||
expectParse("0xffb0", 65472.0) // Rounds up
|
||||
expectParse("0xffc0", 65472.0) // Exact (even)
|
||||
expectParse("0xffd0", 65472.0) // Rounds down
|
||||
expectParse("0xffe0", Float16.greatestFiniteMagnitude) // Exact (odd)
|
||||
// Rationale for the assertions below:
|
||||
// * Float16.greatestFiniteMagnitude has an odd significand
|
||||
// * Let epsilon = the difference between Float16.greatestFiniteMagnitude and its immediate predecessor
|
||||
// * Define a synthetic finite successor to Float16.gFM as Float16.gFM + epsilon
|
||||
// * Assertion: the value above should round to infinity
|
||||
// * Assertion: the value above should be treated as having an even significand
|
||||
// * Conclusion: Exact halfway between Float16.gFM and lIM is the smallest magnitude that should round to infinity
|
||||
// Note: Old Swift implementation gets these wrong
|
||||
expectParse("0xffe0.00000001", Float16.greatestFiniteMagnitude)
|
||||
expectParse("0xffe8.0", Float16.greatestFiniteMagnitude)
|
||||
expectParse("0xffef.0", Float16.greatestFiniteMagnitude)
|
||||
expectParse("0xffef.f", Float16.greatestFiniteMagnitude)
|
||||
expectParse("0xffef.ff", Float16.greatestFiniteMagnitude)
|
||||
expectParse("0xffef.ff8", Float16.greatestFiniteMagnitude)
|
||||
expectParse("0xffef.ffc", Float16.greatestFiniteMagnitude)
|
||||
expectParse("0xffef.fff", Float16.greatestFiniteMagnitude)
|
||||
expectParse("0xffef.fffff", Float16.greatestFiniteMagnitude)
|
||||
expectParse("0xffef.fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", Float16.greatestFiniteMagnitude)
|
||||
expectParse("0xfff0.0", Float16.infinity) // "Rounds up" by rationale above
|
||||
expectParse("0x10000.0", Float16.infinity) // .gFM + epsilon above
|
||||
}
|
||||
|
||||
tests.test("Decimal Floats") {
|
||||
expectParseFails(".")
|
||||
expectParse("0.0", 0.0)
|
||||
expectParse("0", 0.0)
|
||||
expectParse("0.", 0.0)
|
||||
expectParse(".0", 0.0)
|
||||
expectParse("1", 1.0)
|
||||
expectParse("2", 2.0)
|
||||
expectParse("1e0", 1.0)
|
||||
expectParse("3.7e1", 37.0)
|
||||
expectParse("12.34e3", 12336.0)
|
||||
expectParse("-00.0047e5", -470.0)
|
||||
expectParse("2e0", 2.0)
|
||||
expectParse("1e1", 10.0)
|
||||
expectParse("7e1", 70.0)
|
||||
expectParse("1e2", 100.0)
|
||||
expectParse("1e3", 1000.0)
|
||||
expectParse("1e4", 10000.0)
|
||||
expectParse("1e5", Float16.infinity)
|
||||
expectParse("1e6", Float16.infinity)
|
||||
expectParse("1e7", Float16.infinity)
|
||||
expectParse("1e9999999999999999999999999999999999", Float16.infinity)
|
||||
expectParse("1e0000000000000000000000000000000001", 10.0)
|
||||
expectParse("1", 1.0)
|
||||
expectParse("1.0", 1.0)
|
||||
expectParse("1.00000000", 1.0)
|
||||
expectParse("2.0", 2.0)
|
||||
expectParse("0.000001", 1e-6)
|
||||
expectParse("0.0000001", 1e-7)
|
||||
expectParse("0.00000001", 0.0)
|
||||
expectParse("0.000000001", 0.0)
|
||||
expectParse("0.0000000001", 0.0)
|
||||
expectParse("0.00000000001", 0.0)
|
||||
expectParse("0.000000000001", 0.0)
|
||||
expectParse("0.0000000000001", 0.0)
|
||||
expectParse("0.00000000000001", 0.0)
|
||||
}
|
||||
|
||||
tests.test("Exhaustive Float16") {
|
||||
for i in 0..<0xffff {
|
||||
let f = Float16(bitPattern: UInt16(i))
|
||||
let s = f.debugDescription
|
||||
if !f.isNaN {
|
||||
expectParse(s, f)
|
||||
} else {
|
||||
let r = Float16(Substring(s))!
|
||||
expectTrue(r.isNaN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
352
test/stdlib/ParseFloat32.swift
Normal file
352
test/stdlib/ParseFloat32.swift
Normal file
@@ -0,0 +1,352 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %target-build-swift -g %s -o %t/a.out -enable-experimental-feature Extern
|
||||
// RUN: %target-codesign %t/a.out
|
||||
// RUN: %target-run %t/a.out
|
||||
// REQUIRES: executable_test
|
||||
|
||||
// Needed to verify the legacy ABI
|
||||
// REQUIRES: swift_feature_Extern
|
||||
|
||||
import StdlibUnittest
|
||||
|
||||
let tests = TestSuite("FloatingPointParsing")
|
||||
|
||||
fileprivate func expectRoundTrip(
|
||||
_ value: Float32,
|
||||
stackTrace: SourceLocStack = SourceLocStack(),
|
||||
showFrame: Bool = true,
|
||||
file: String = #file, line: UInt = #line
|
||||
) {
|
||||
let text = value.debugDescription
|
||||
let roundTrip = Float32(Substring(text))
|
||||
expectNotNil(roundTrip, text, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
if let roundTrip {
|
||||
if value.isNaN {
|
||||
// We cannot in general guarantee perfect round-tripping for NaN values,
|
||||
// but we can verify that printing/parsing a NaN does result in another
|
||||
// NaN.
|
||||
expectTrue(roundTrip.isNaN, text, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
} else {
|
||||
expectEqual(roundTrip.bitPattern, value.bitPattern, text, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func expectParse(
|
||||
_ input: String,
|
||||
_ expected: Float32,
|
||||
stackTrace: SourceLocStack = SourceLocStack(),
|
||||
showFrame: Bool = true,
|
||||
file: String = #file, line: UInt = #line
|
||||
) {
|
||||
let parsed = Float32(Substring(input))
|
||||
expectNotNil(parsed, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
if let parsed {
|
||||
expectEqual(parsed.bitPattern, expected.bitPattern, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
func expectParseFails(
|
||||
_ input: String,
|
||||
stackTrace: SourceLocStack = SourceLocStack(),
|
||||
showFrame: Bool = true,
|
||||
file: String = #file, line: UInt = #line
|
||||
) {
|
||||
let parsed = Float32(Substring(input))
|
||||
expectNil(parsed, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
}
|
||||
|
||||
tests.test("Invalids") {
|
||||
expectParseFails("")
|
||||
expectParseFails(".")
|
||||
expectParseFails("e0")
|
||||
expectParseFails(".e0")
|
||||
expectParseFails("1e+")
|
||||
expectParseFails("-")
|
||||
expectParseFails("+")
|
||||
expectParseFails("&")
|
||||
expectParseFails("+x")
|
||||
expectParseFails("x")
|
||||
}
|
||||
|
||||
tests.test("Infinities") {
|
||||
expectParse("inf", Float32.infinity)
|
||||
expectParse("+inf", Float32.infinity)
|
||||
expectParse("-inf", -Float32.infinity)
|
||||
expectParse("INF", Float32.infinity)
|
||||
expectParse("InF", Float32.infinity)
|
||||
expectParse("iNf", Float32.infinity)
|
||||
expectParse("infinity", Float32.infinity)
|
||||
expectParse("INFINITY", Float32.infinity)
|
||||
expectParse("+infinity", Float32.infinity)
|
||||
expectParse("-infinity", -Float32.infinity)
|
||||
|
||||
expectParseFails("i")
|
||||
expectParseFails("in")
|
||||
expectParseFails(" inf")
|
||||
expectParseFails("- inf")
|
||||
expectParseFails("--inf")
|
||||
expectParseFails("-+inf")
|
||||
expectParseFails("++inf")
|
||||
expectParseFails("inf ")
|
||||
expectParseFails("inx")
|
||||
expectParseFails("-inx")
|
||||
expectParseFails("infi")
|
||||
expectParseFails("infin")
|
||||
expectParseFails("infini")
|
||||
expectParseFails("infinit")
|
||||
expectParseFails("infinite")
|
||||
expectParseFails("infinityandbeyond")
|
||||
|
||||
expectRoundTrip(Float32.infinity)
|
||||
expectRoundTrip(-Float32.infinity)
|
||||
}
|
||||
|
||||
tests.test("NaNs") {
|
||||
// Note: Previous Swift runtime used libc strtof and then
|
||||
// truncated to Float32, which is why some of these are
|
||||
// wrong when testing previous runtimes.
|
||||
expectRoundTrip(Float32.nan)
|
||||
expectRoundTrip(-Float32.nan)
|
||||
expectRoundTrip(Float32(nan:73, signaling:false))
|
||||
expectRoundTrip(Float32(nan:73, signaling:true))
|
||||
expectParse("nan", Float32.nan)
|
||||
expectParse("NAN", Float32.nan)
|
||||
expectParse("NaN", Float32.nan)
|
||||
expectParse("-nan", -Float32.nan)
|
||||
expectParse("nan()", Float32.nan)
|
||||
expectParse("nan(0)", Float32.nan)
|
||||
expectParse("nan(000000000000000000000000000000000000000)", Float32.nan)
|
||||
expectParse("nan(0x00000000000000000000000000000000000000)", Float32.nan)
|
||||
expectParse("nan(10)", Float32(nan:10, signaling:false))
|
||||
expectParse("nan(0x10)", Float32(nan:16, signaling:false))
|
||||
expectParse("nan(010)", Float32(nan:8, signaling:false))
|
||||
expectParse("nan(9)", Float32(nan:9, signaling:false))
|
||||
expectParse("nan(99)", Float32(nan:99, signaling:false))
|
||||
expectParse("nan(255)", Float32(nan:255, signaling:false))
|
||||
expectParse("nan(256)", Float32(nan:256, signaling:false))
|
||||
expectParse("nan(511)", Float32(nan:511, signaling:false))
|
||||
expectParse("nan(999999)", Float32(nan:999999, signaling:false))
|
||||
expectParse("nan(999999999999999)", Float32(nan:0x67fff, signaling:false))
|
||||
expectParse("nan(0xfffffffffffff)", Float32(nan:0x1fffff, signaling:false))
|
||||
expectParseFails("n")
|
||||
expectParseFails("na")
|
||||
expectParseFails("nann")
|
||||
expectParseFails("nananananana")
|
||||
}
|
||||
|
||||
tests.test("HexFloats") {
|
||||
expectParseFails("0x")
|
||||
expectParseFails("0x.")
|
||||
expectParseFails("0x😀")
|
||||
expectParseFails("0x1😀p2")
|
||||
expectParseFails("0x1.07😀p2")
|
||||
expectParseFails("0x1p😀")
|
||||
expectParseFails("0x1p+😀")
|
||||
expectParseFails("0x1p")
|
||||
expectParseFails("0x1p+")
|
||||
expectParseFails("0xp+7")
|
||||
expectParseFails("0x.p1")
|
||||
expectParseFails("0x..p1")
|
||||
expectParseFails("0x0p1.0")
|
||||
expectParse("0x0p0", 0.0)
|
||||
expectParse("0x0p1", 0.0)
|
||||
expectParse("-0x0p0", -0.0)
|
||||
expectParse("0x0p999999999", 0.0)
|
||||
expectParse("0x0.0p999999999", 0.0)
|
||||
expectParse("0x.0p-999999999", 0.0)
|
||||
expectParse("0x0p-999999999", 0.0)
|
||||
|
||||
expectParse("0x.000001", 5.9604645e-08)
|
||||
expectParse("0x1p-150", 0.0)
|
||||
expectParse("0x1p-149", Float32.leastNonzeroMagnitude)
|
||||
expectParse("0x1p-149", Float32(bitPattern:1))
|
||||
expectParse("0x1p-148", Float32(bitPattern:2))
|
||||
expectParse("0x1p-147", Float32(bitPattern:4))
|
||||
expectParse("0x1p-146", Float32(bitPattern:8))
|
||||
expectParse("0x1p-145", Float32(bitPattern:16))
|
||||
|
||||
// Test the tricky rounding of values between the largest subnormal and least normal
|
||||
expectParse("0x1.fffffcp-127", Float32.leastNormalMagnitude.nextDown) // Largest subnormal
|
||||
expectParse("0x0.fffffep-126", Float32.leastNormalMagnitude.nextDown) // Largest subnormal
|
||||
expectParse("0x0.fffffefffffp-126", Float32.leastNormalMagnitude.nextDown) // Just less than halfway
|
||||
expectParse("0x0.ffffffp-126", Float32.leastNormalMagnitude) // Halfway
|
||||
expectParse("0x1.fffffep-127", Float32.leastNormalMagnitude) // Halfway
|
||||
expectParse("0x0.ffffff0000000001p-126", Float32.leastNormalMagnitude) // Just above halfway
|
||||
expectParse("0x1p-126", Float32.leastNormalMagnitude) // Smallest normal
|
||||
|
||||
expectParse("0x1.555556p-2", 1.0/3.0)
|
||||
expectParse("0x0.ffffff", Float(1.0).nextDown) // Exactly
|
||||
expectParse("0x0.ffffff8", 1.0) // Halfway
|
||||
expectParse("0x1p0", 1.0)
|
||||
expectParse("0x1.000001p0", 1.0) // Halfway, rounds even
|
||||
expectParse("0x1.00000100000000000000000000000000000000000000000000001p0", Float(1.0).nextUp) // Halfway + epsilon
|
||||
expectParse("0x1.000002p0", Float(1.0).nextUp) // Exactly
|
||||
expectParse("0x1.00000200000000001", Float(1.0).nextUp) // Bigger than above
|
||||
expectParse("0x1000002000000000000000000000001p-120", (Float(1.0)).nextUp)
|
||||
expectParse("0x00000000.000000000000000000000000000001000002000000000000000000001p120", Float(1.0).nextUp)
|
||||
expectParse("0x1p+1", 2.0)
|
||||
expectParse("0x1p+0000000000000000000000000000000000001", 2.0)
|
||||
expectParse("0x12", 18.0)
|
||||
expectParse("0xab", 171.0)
|
||||
expectParse("0x1p+10", 1024.0)
|
||||
expectParse("0x1p+0000000000000000000000000010", 1024.0)
|
||||
expectParse("0x1.921fb4p+1", Float32.pi)
|
||||
|
||||
// Rationale for the four assertions below:
|
||||
// * Float32.greatestFiniteMagnitude has an odd significand
|
||||
// * Let epsilon = the difference between Float32.greatestFiniteMagnitude and its immediate predecessor
|
||||
// * Define a synthetic finite successor to Float32.gFM as Float32.gFM + epsilon
|
||||
// * Assertion: the value above should round to infinity
|
||||
// * Assertion: the value above should be treated as having an even significand
|
||||
// * Conclusion: Exact halfway between Float32.gFM and lIM is the smallest magnitude that should round to infinity
|
||||
expectParse("0x1.fffffep+127", Float32.greatestFiniteMagnitude) // Exact
|
||||
expectParse("0x1.fffffefffffffffffffffffffffp+127", Float32.greatestFiniteMagnitude) // .gFM + less than 1/2 epsilon
|
||||
expectParse("0x1.ffffffp+127", Float32.infinity) // .gFM + 1/2 epsilon
|
||||
expectParse("0x2.000000p+127", Float32.infinity) // .gFM + epsilon above
|
||||
|
||||
expectParse("0x123456789abcdefp123456789", Float32.infinity)
|
||||
}
|
||||
|
||||
tests.test("Decimal Floats") {
|
||||
expectParse("9007199254740992.0", 9007199254740992.0)
|
||||
expectParse("-9007199254740992.0", -9007199254740992.0)
|
||||
expectParse("4503599627370496.0", 4503599627370496.0)
|
||||
expectParse("7.888609052210118e-31", 7.888609052210118e-31)
|
||||
expectParse("3.944304526105059e-31", 3.944304526105059e-31)
|
||||
expectParse(".0", 0.0)
|
||||
expectParse("0", 0.0)
|
||||
expectParse("0.", 0.0)
|
||||
expectParse("0.0", 0.0)
|
||||
expectParse("000000000000000000000000000000", 0.0)
|
||||
expectParse(".000000000000000000000000000000", 0.0)
|
||||
expectParse("000000000000000000000000000000.0000000000000000000000000000", 0.0)
|
||||
expectParse("1", 1.0)
|
||||
expectParse("2", 2.0)
|
||||
expectParse("1e0", 1.0)
|
||||
expectParse("3.7e1", 37.0)
|
||||
expectParse("12.34e3", 12340.0)
|
||||
expectParse("-00.0047e5", -470.0)
|
||||
expectParse("2e0", 2.0)
|
||||
expectParse("1e1", 10.0)
|
||||
expectParse("7e1", 70.0)
|
||||
expectParse("1e2", 100.0)
|
||||
expectParse("1e3", 1000.0)
|
||||
expectParse("1e4", 10000.0)
|
||||
expectParse("1e0000000000000000000000000000000001", 10.0)
|
||||
expectParse("1", 1.0)
|
||||
expectParse("1.0", 1.0)
|
||||
expectParse("1.00000000", 1.0)
|
||||
expectParse("2.0", 2.0)
|
||||
|
||||
expectParse("0.000001", 1e-6)
|
||||
expectParse("0.0000001", 1e-7)
|
||||
expectParse("0.00000001", 1e-8)
|
||||
expectParse("0.000000001", 1e-9)
|
||||
expectParse("0.0000000001", 1e-10)
|
||||
expectParse("0.00000000001", 1e-11)
|
||||
expectParse("0.000000000001", 1e-12)
|
||||
expectParse("0.0000000000001", 1e-13)
|
||||
expectParse("0.00000000000001", 1e-14)
|
||||
expectParse("1e-45", Float32.leastNonzeroMagnitude)
|
||||
// Exact decimal form of 2^-149 (which is exactly Float32.leastNonzeroMagnitude)
|
||||
expectParse("0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125", Float32.leastNonzeroMagnitude)
|
||||
// Exact decimal form of 2^-150 (halfway between Float32.lNM and 0)
|
||||
// Ties round even, so this rounds down to zero
|
||||
expectParse("0.000000000000000000000000000000000000000000000700649232162408535461864791644958065640130970938257885878534141944895541342930300743319094181060791015625", 0.0)
|
||||
// Increment the last digit, this should round up
|
||||
expectParse("0.000000000000000000000000000000000000000000000700649232162408535461864791644958065640130970938257885878534141944895541342930300743319094181060791015626", Float32.leastNonzeroMagnitude)
|
||||
// Tiny tiny tiny bit larger than halfway between Float32.lNM and 0, so rounds up
|
||||
expectParse("0.00000000000000000000000000000000000000000000070064923216240853546186479164495806564013097093825788587853414194489554134293030074331909418106079101562500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", Float32.leastNonzeroMagnitude)
|
||||
expectParse("00000000000000000000000000000000000000000000070064923216240853546186479164495806564013097093825788587853414194489554134293030074331909418106079101562500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.0e-320", Float32.leastNonzeroMagnitude)
|
||||
expectParse("11754942e-45", Float32.leastNormalMagnitude.nextDown)
|
||||
expectParse("117549432e-46", Float32.leastNormalMagnitude)
|
||||
expectParse("34028235e+31", Float32.greatestFiniteMagnitude)
|
||||
expectParse("-34028235e+31", -Float32.greatestFiniteMagnitude)
|
||||
expectParse("340282346638528859811704183484516925439", Float32.greatestFiniteMagnitude) // Exact - 1
|
||||
expectParse("340282346638528859811704183484516925440", Float32.greatestFiniteMagnitude) // Exact
|
||||
expectParse("340282346638528859811704183484516925441", Float32.greatestFiniteMagnitude) // Exact + 1
|
||||
// 1 less than exact midpoint between gFM and gFM + 1 ULP
|
||||
// (Largest integer that rounds to gFM)
|
||||
expectParse("340282356779733661637539395458142568447", Float32.greatestFiniteMagnitude)
|
||||
expectParse("340282356779733661637539395458142568447.99999999999999999999999999999999999999999999999999999", Float32.greatestFiniteMagnitude)
|
||||
// Exact midpoint between gFM and gFM + 1 ULP
|
||||
// (Rounds even to gFM + 1 ULP, which we treat as infinite
|
||||
expectParse("340282356779733661637539395458142568448", Float32.infinity)
|
||||
expectParse("340282356779733661637539395458142568448.000000000000000000000000000000000000000000000000", Float32.infinity)
|
||||
expectParse("340282356779733661637539395458142568448.000000000000000000000000000000000000000000000001", Float32.infinity)
|
||||
expectParse("3.4028235e+38", Float32.greatestFiniteMagnitude)
|
||||
expectParse("3.4028236e+38", Float32.infinity)
|
||||
expectParse("3.4028237e+38", Float32.infinity)
|
||||
expectParse("3.402824e+38", Float32.infinity)
|
||||
expectParse("3.40283e+38", Float32.infinity)
|
||||
expectParse("3.4029e+38", Float32.infinity)
|
||||
expectParse("3.403e+38", Float32.infinity)
|
||||
expectParse("3.41e+38", Float32.infinity)
|
||||
expectParse("3.5e+38", Float32.infinity)
|
||||
expectParse("4e+38", Float32.infinity)
|
||||
expectParse("1e45", Float32.infinity)
|
||||
expectParse("1e309", Float32.infinity)
|
||||
expectParse("1e9999999999999999999999999999999999", Float32.infinity)
|
||||
expectParse("999999999999999999999999999999999999999.999999999999999999999999999", Float32.infinity)
|
||||
expectParse("7674047411400702925974988342550565582448.117", Float32.infinity)
|
||||
}
|
||||
|
||||
@_extern(c, "_swift_stdlib_strtof_clocale")
|
||||
func _swift_stdlib_strtof_clocale(
|
||||
_: Optional<UnsafePointer<CChar>>,
|
||||
_: Optional<UnsafeMutablePointer<Float32>>
|
||||
) -> Optional<UnsafePointer<CChar>>
|
||||
|
||||
func viaLegacy(_ text: String) -> Float32? {
|
||||
return text.withCString { strptr -> Float32? in
|
||||
var result = Float32()
|
||||
let succeeded = withUnsafeMutablePointer(to: &result) { dptr in
|
||||
let endptr = _swift_stdlib_strtof_clocale(strptr, dptr)
|
||||
return endptr == strptr + text.utf8.count
|
||||
}
|
||||
if succeeded {
|
||||
return Float32?.some(result)
|
||||
} else {
|
||||
return Float32?.none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tests.test("Legacy ABI") {
|
||||
expectEqual(viaLegacy("1.0"), 1.0 as Float32)
|
||||
expectEqual(viaLegacy("3.4028235e+38"), Float32.greatestFiniteMagnitude)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// Verify round-trip correctness for every Float32 value.
|
||||
// Only enable this locally!
|
||||
tests.test("Exhaustive Float32") {
|
||||
for i in 0..<0xffffffff {
|
||||
let f = Float32(bitPattern: UInt32(i))
|
||||
expectRoundTrip(f)
|
||||
}
|
||||
expectRoundTrip(Float32(bitPattern: 0xffffffff)
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// Checking 100 million random floats takes only a fraction of a second on a
|
||||
// release build, but is _PAINFULLY SLOW_ in debug builds, so only enable this
|
||||
// locally!
|
||||
tests.test("Random Float32") {
|
||||
let blocks = 100_000
|
||||
let blocksize = 1_000
|
||||
for _ in 0..<blocks {
|
||||
var raw = UInt32.random(in: 0...UInt32.max)
|
||||
for _ in 0..<blocksize {
|
||||
raw &+= 1
|
||||
let d = Float32(bitPattern: raw)
|
||||
expectRoundTrip(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
runAllTests()
|
||||
375
test/stdlib/ParseFloat64.swift
Normal file
375
test/stdlib/ParseFloat64.swift
Normal file
@@ -0,0 +1,375 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %target-build-swift -g %s -o %t/a.out -enable-experimental-feature Extern
|
||||
// RUN: %target-codesign %t/a.out
|
||||
// RUN: %target-run %t/a.out
|
||||
|
||||
// REQUIRES: executable_test
|
||||
|
||||
// TODO: Figure out why this test breaks on wasm32
|
||||
// UNSUPPORTED: CPU=wasm32
|
||||
|
||||
// Needed to declare the ABI entry point
|
||||
// REQUIRES: swift_feature_Extern
|
||||
|
||||
import StdlibUnittest
|
||||
|
||||
let tests = TestSuite("FloatingPointParsing")
|
||||
|
||||
fileprivate func expectRoundTrip(
|
||||
_ value: Float64,
|
||||
stackTrace: SourceLocStack = SourceLocStack(),
|
||||
showFrame: Bool = true,
|
||||
file: String = #file, line: UInt = #line
|
||||
) {
|
||||
let text = value.debugDescription
|
||||
let roundTrip = Float64(Substring(text))
|
||||
expectNotNil(roundTrip, text, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
if let roundTrip {
|
||||
if value.isNaN {
|
||||
// We cannot in general guarantee perfect round-tripping for NaN values,
|
||||
// but we can verify that printing/parsing a NaN does result in another
|
||||
// NaN.
|
||||
expectTrue(roundTrip.isNaN, text, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
} else {
|
||||
expectEqual(roundTrip.bitPattern, value.bitPattern, text, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func expectParse(
|
||||
_ input: String,
|
||||
_ expected: Float64,
|
||||
stackTrace: SourceLocStack = SourceLocStack(),
|
||||
showFrame: Bool = true,
|
||||
file: String = #file, line: UInt = #line
|
||||
) {
|
||||
let parsed = Float64(Substring(input))
|
||||
let msg = "\(input) did not parse to \(expected)"
|
||||
expectNotNil(parsed, msg, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
if let parsed {
|
||||
expectEqual(parsed.bitPattern, expected.bitPattern, msg, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
func expectParseFails(
|
||||
_ input: String,
|
||||
stackTrace: SourceLocStack = SourceLocStack(),
|
||||
showFrame: Bool = true,
|
||||
file: String = #file, line: UInt = #line
|
||||
) {
|
||||
let parsed = Float64(Substring(input))
|
||||
expectNil(parsed, stackTrace: stackTrace, showFrame: showFrame, file: file, line: line)
|
||||
}
|
||||
|
||||
tests.test("Invalids") {
|
||||
expectParseFails("")
|
||||
expectParseFails(".")
|
||||
expectParseFails("e0")
|
||||
expectParseFails(".e0")
|
||||
expectParseFails("1e+")
|
||||
expectParseFails("-")
|
||||
expectParseFails("+")
|
||||
expectParseFails("&")
|
||||
expectParseFails("+x")
|
||||
expectParseFails("x")
|
||||
}
|
||||
|
||||
tests.test("Infinities") {
|
||||
expectParse("inf", Float64.infinity)
|
||||
expectParse("+inf", Float64.infinity)
|
||||
expectParse("-inf", -Float64.infinity)
|
||||
expectParse("INF", Float64.infinity)
|
||||
expectParse("InF", Float64.infinity)
|
||||
expectParse("iNf", Float64.infinity)
|
||||
expectParse("infinity", Float64.infinity)
|
||||
expectParse("INFINITY", Float64.infinity)
|
||||
expectParse("+infinity", Float64.infinity)
|
||||
expectParse("-infinity", -Float64.infinity)
|
||||
|
||||
expectParseFails("i")
|
||||
expectParseFails("in")
|
||||
expectParseFails(" inf")
|
||||
expectParseFails("- inf")
|
||||
expectParseFails("--inf")
|
||||
expectParseFails("-+inf")
|
||||
expectParseFails("++inf")
|
||||
expectParseFails("inf ")
|
||||
expectParseFails("inx")
|
||||
expectParseFails("-inx")
|
||||
expectParseFails("infi")
|
||||
expectParseFails("infin")
|
||||
expectParseFails("infini")
|
||||
expectParseFails("infinit")
|
||||
expectParseFails("infinite")
|
||||
expectParseFails("infinityandbeyond")
|
||||
|
||||
expectRoundTrip(Float64.infinity)
|
||||
expectRoundTrip(-Float64.infinity)
|
||||
}
|
||||
|
||||
tests.test("NaNs") {
|
||||
// Note: Previous Swift runtime used libc strtof and then
|
||||
// truncated to Float64, which is why some of these are
|
||||
// wrong when testing previous runtimes.
|
||||
expectRoundTrip(Float64.nan)
|
||||
expectRoundTrip(-Float64.nan)
|
||||
expectRoundTrip(Float64(nan:73, signaling:false))
|
||||
expectRoundTrip(Float64(nan:73, signaling:true))
|
||||
expectParse("nan", Float64.nan)
|
||||
expectParse("NAN", Float64.nan)
|
||||
expectParse("NaN", Float64.nan)
|
||||
expectParse("-nan", -Float64.nan)
|
||||
expectParse("nan()", Float64.nan)
|
||||
expectParse("nan(0)", Float64.nan)
|
||||
expectParse("nan(000000000000000000000000000000000000000)", Float64.nan)
|
||||
expectParse("nan(0x00000000000000000000000000000000000000)", Float64.nan)
|
||||
expectParse("nan(10)", Float64(nan:10, signaling:false))
|
||||
expectParse("nan(1234567890)", Float64(nan:1234567890, signaling:false))
|
||||
expectParse("nan(0x10)", Float64(nan:16, signaling:false))
|
||||
expectParse("nan(0x12345678)", Float64(nan:0x12345678, signaling:false))
|
||||
expectParse("nan(0x9abcdef)", Float64(nan:0x9abcdef, signaling:false))
|
||||
expectParse("nan(010)", Float64(nan:8, signaling:false))
|
||||
expectParse("nan(01234567)", Float64(nan:0o1234567, signaling:false))
|
||||
expectParse("nan(9)", Float64(nan:9, signaling:false))
|
||||
expectParse("nan(99)", Float64(nan:99, signaling:false))
|
||||
expectParse("nan(255)", Float64(nan:255, signaling:false))
|
||||
expectParse("nan(256)", Float64(nan:256, signaling:false))
|
||||
expectParse("nan(511)", Float64(nan:511, signaling:false))
|
||||
expectParse("nan(999999)", Float64(nan:999999, signaling:false))
|
||||
expectParse("nan(999999999999999)", Float64(nan:0x38d7ea4c67fff, signaling:false))
|
||||
expectParse("nan(0xfffffffffffff)", Float64(nan:0x3ffffffffffff, signaling:false))
|
||||
expectParseFails("n")
|
||||
expectParseFails("na")
|
||||
expectParseFails("nann")
|
||||
expectParseFails("nananananana")
|
||||
expectParseFails("nan(xyz)")
|
||||
expectParseFails("nan(01238)")
|
||||
expectParseFails("nan(01239)")
|
||||
expectParseFails("nan(0123a)")
|
||||
expectParseFails("nan(0123b)")
|
||||
expectParseFails("nan(0123c)")
|
||||
expectParseFails("nan(0123d)")
|
||||
expectParseFails("nan(0123e)")
|
||||
expectParseFails("nan(0123f)")
|
||||
expectParseFails("nan(0123g)")
|
||||
expectParseFails("nan(123a)")
|
||||
expectParseFails("nan(123b)")
|
||||
expectParseFails("nan(123c)")
|
||||
expectParseFails("nan(123d)")
|
||||
expectParseFails("nan(123e)")
|
||||
expectParseFails("nan(123f)")
|
||||
expectParseFails("nan(123g)")
|
||||
expectParseFails("nan(0x123g)")
|
||||
}
|
||||
|
||||
tests.test("HexFloats") {
|
||||
expectParseFails("0x")
|
||||
expectParseFails("0x.")
|
||||
expectParseFails("0x😀")
|
||||
expectParseFails("0x1😀p2")
|
||||
expectParseFails("0x1.07😀p2")
|
||||
expectParseFails("0x1p😀")
|
||||
expectParseFails("0x1p+😀")
|
||||
expectParseFails("0x1p")
|
||||
expectParseFails("0x1p+")
|
||||
expectParseFails("0xp+7")
|
||||
expectParseFails("0x.p1")
|
||||
expectParseFails("0x..p1")
|
||||
expectParseFails("0x0p1.0")
|
||||
expectParse("0x0p0", 0.0)
|
||||
expectParse("0x0p1", 0.0)
|
||||
expectParse("-0x0p0", -0.0)
|
||||
expectParse("0x0p999999999", 0.0)
|
||||
expectParse("0x0.0p999999999", 0.0)
|
||||
expectParse("0x.0p-999999999", 0.0)
|
||||
expectParse("0x0p-999999999", 0.0)
|
||||
|
||||
expectParse("0x.000001", 0x0.000001p0)
|
||||
expectParse("0x1p-1074", Float64.leastNonzeroMagnitude)
|
||||
expectParse("0x1p-1074", Float64(bitPattern:1))
|
||||
expectParse("0x1p-1073", Float64(bitPattern:2))
|
||||
expectParse("0x1p-1072", Float64(bitPattern:4))
|
||||
expectParse("0x1p-1071", Float64(bitPattern:8))
|
||||
expectParse("0x1p-1070", Float64(bitPattern:16))
|
||||
|
||||
// Test the tricky rounding of values between the largest subnormal and least normal
|
||||
expectParse("0x0.fffffffffffffp-1022", Float64.leastNormalMagnitude.nextDown) // Largest subnormal
|
||||
expectParse("0x1.ffffffffffffep-1023", Float64.leastNormalMagnitude.nextDown) // Largest subnormal
|
||||
expectParse("0x1.ffffffffffffefffffffffffffffffffffp-1023", Float64.leastNormalMagnitude.nextDown) // Largest subnormal
|
||||
expectParse("0x1.fffffffffffffp-1023", Float64.leastNormalMagnitude) // Halfway
|
||||
expectParse("0x1.fffffffffffff00000000000000001p-1023", Float64.leastNormalMagnitude) // just above Halfway
|
||||
|
||||
expectParse("0x1p-1022", Float64.leastNormalMagnitude) // Smallest normal
|
||||
|
||||
expectParse("0x1.5555555555555p-2", 1.0/3.0)
|
||||
expectParse("0x0.fffffffffffff8", Float64(1.0).nextDown) // Exactly
|
||||
expectParse("0x0.fffffffffffffc", 1.0) // Halfway
|
||||
expectParse("0x1p0", 1.0)
|
||||
|
||||
expectParse("0x1.00000000000008p0", 1.0) // Halfway, rounds even
|
||||
expectParse("0x1.0000000000000800000000000000000000000000000000000000000000001p0", Float64(1.0).nextUp) // Halfway + epsilon
|
||||
expectParse("0x1.0000000000001p0", Float64(1.0).nextUp) // Exactly
|
||||
expectParse("0x1.000000000000100000000001", Float64(1.0).nextUp) // Bigger than above
|
||||
expectParse("0x1000000000000100000000000000001p-120", (Float64(1.0)).nextUp)
|
||||
expectParse("0x00000000.0000000000000000000000000000010000000000001000000000000000000001p120", Float64(1.0).nextUp)
|
||||
expectParse("0x1p+1", 2.0)
|
||||
expectParse("0x1p+0000000000000000000000000000000000001", 2.0)
|
||||
expectParse("0x12", 18.0)
|
||||
expectParse("0xab", 171.0)
|
||||
expectParse("0x1p+10", 1024.0)
|
||||
expectParse("0x1p+0000000000000000000000000010", 1024.0)
|
||||
expectParse("0x1.921fb54442d18p+1", Float64.pi)
|
||||
|
||||
// Rationale for the four assertions below:
|
||||
// * Float64.greatestFiniteMagnitude has an odd significand
|
||||
// * Let epsilon = the difference between Float64.greatestFiniteMagnitude and its immediate predecessor
|
||||
// * Define a synthetic finite successor to Float64.gFM as Float64.gFM + epsilon
|
||||
// * Assertion: the value above should round to infinity
|
||||
// * Assertion: the value above should be treated as having an even significand
|
||||
// * Conclusion: Exact halfway between Float64.gFM and the value above is the smallest magnitude that should round to infinity
|
||||
expectParse("0x1.fffffffffffffp+1023", Float64.greatestFiniteMagnitude) // Exact
|
||||
expectParse("0x1.fffffffffffff7fffffffffffffffffffffp+1023", Float64.greatestFiniteMagnitude) // .gFM + less than 1/2 epsilon
|
||||
expectParse("0x1.fffffffffffff8p+1023", Float64.infinity) // .gFM + 1/2 epsilon
|
||||
expectParse("0x2.0000000000000p+1023", Float64.infinity) // .gFM + epsilon above
|
||||
|
||||
expectParse("0x123456789abcdefp123456789", Float64.infinity)
|
||||
}
|
||||
|
||||
tests.test("Decimal Floats") {
|
||||
expectParse("1e-163", 1e-163)
|
||||
expectParse("9007199254740992.0", 9007199254740992.0)
|
||||
expectParse("-9007199254740992.0", -9007199254740992.0)
|
||||
expectParse("4503599627370496.0", 4503599627370496.0)
|
||||
expectParse("7.888609052210118e-31", 7.888609052210118e-31)
|
||||
expectParse("3.944304526105059e-31", 3.944304526105059e-31)
|
||||
expectParse(".0", 0.0)
|
||||
expectParse("0", 0.0)
|
||||
expectParse("0.", 0.0)
|
||||
expectParse("0.0", 0.0)
|
||||
expectParse("000000000000000000000000000000", 0.0)
|
||||
expectParse(".000000000000000000000000000000", 0.0)
|
||||
expectParse("000000000000000000000000000000.0000000000000000000000000000", 0.0)
|
||||
expectParse("0000000000000000.000000000000000e9999999999", 0.0)
|
||||
expectParse("0e9999999999", 0.0)
|
||||
expectParse("1", 1.0)
|
||||
expectParse("2", 2.0)
|
||||
expectParse("1e0", 1.0)
|
||||
expectParse("3.7e1", 37.0)
|
||||
expectParse("12.34e3", 12340.0)
|
||||
expectParse("-00.0047e5", -470.0)
|
||||
expectParse("2e0", 2.0)
|
||||
expectParse("1e1", 10.0)
|
||||
expectParse("7e1", 70.0)
|
||||
expectParse("1e2", 100.0)
|
||||
expectParse("1e3", 1000.0)
|
||||
expectParse("1e4", 10000.0)
|
||||
expectParse("1e0000000000000000000000000000000001", 10.0)
|
||||
expectParse("1", 1.0)
|
||||
expectParse("1.0", 1.0)
|
||||
expectParse("1.00000000", 1.0)
|
||||
expectParse("2.0", 2.0)
|
||||
|
||||
expectParse("0.000001", 1e-6)
|
||||
expectParse("0.0000001", 1e-7)
|
||||
expectParse("0.00000001", 1e-8)
|
||||
expectParse("0.000000001", 1e-9)
|
||||
expectParse("0.0000000001", 1e-10)
|
||||
expectParse("0.00000000001", 1e-11)
|
||||
expectParse("0.000000000001", 1e-12)
|
||||
expectParse("0.0000000000001", 1e-13)
|
||||
expectParse("0.00000000000001", 1e-14)
|
||||
|
||||
expectParse("5e-324", Float64.leastNonzeroMagnitude)
|
||||
// Exact decimal form of 2^-1074 (which is exactly Float64.leastNonzeroMagnitude)
|
||||
expectParse("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625", Float64.leastNonzeroMagnitude)
|
||||
// Exact decimal form of 2^-1075 (halfway between Float64.lNM and 0)
|
||||
// Ties round even, so this rounds down to zero
|
||||
expectParse("0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125", 0.0)
|
||||
// Increment the last digit, this should round up
|
||||
expectParse("0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328126", Float64.leastNonzeroMagnitude)
|
||||
|
||||
// Even a teeny-tiny bit larger than 2^-1075 should round up
|
||||
expectParse("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002470328229206232720882843964341106861825299013071623822127928412503377536351043759326499181808179961898982823477228588654633283551779698981993873980053909390631503565951557022639229085839244910518443593180284993653615250031937045767824921936562366986365848075700158576926990370631192827955855133292783433840935197801553124659726357957462276646527282722005637400648549997709659947045402082816622623785739345073633900796776193057750674017632467360096895134053553745851666113422376667860416215968046191446729184030053005753084904876539171138659164623952491262365388187963623937328042389101867234849766823508986338858792562830275599565752445550725518931369083625477918694866799496832404970582102851318545139621383772282614543769341253209859132766723632812500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", Float64.leastNonzeroMagnitude)
|
||||
|
||||
expectParse("2.2250738585072009e-308", Float64.leastNormalMagnitude.nextDown)
|
||||
expectParse("2.2250738585072014e-308", Float64.leastNormalMagnitude)
|
||||
expectParse("1.7976931348623157e+308", Float64.greatestFiniteMagnitude)
|
||||
expectParse("-1.7976931348623157e+308", -Float64.greatestFiniteMagnitude)
|
||||
|
||||
expectParse("179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858367", Float64.greatestFiniteMagnitude) // Exact - 1
|
||||
expectParse("179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368", Float64.greatestFiniteMagnitude) // Exact
|
||||
expectParse("179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858369", Float64.greatestFiniteMagnitude) // Exact + 1
|
||||
|
||||
// exact gFM + 1 ULP
|
||||
expectParse("179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216", Float64.infinity)
|
||||
|
||||
// 1 less than exact midpoint between gFM and gFM + 1 ULP
|
||||
// (Largest integer that rounds to gFM)
|
||||
expectParse("179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791", Float64.greatestFiniteMagnitude)
|
||||
// Even closer to (but still less than) the exact midpoint
|
||||
expectParse("179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791.99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", Float64.greatestFiniteMagnitude)
|
||||
|
||||
// Exact midpoint between gFM and gFM + 1 ULP
|
||||
// (Rounds even to gFM + 1 ULP, which we treat as infinite
|
||||
expectParse("179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497792", Float64.infinity)
|
||||
|
||||
expectParse("1.7976931348623157e+308", Float64.greatestFiniteMagnitude)
|
||||
expectParse("1.7976931348623159e+308", Float64.infinity)
|
||||
expectParse("1.797693134862316e+308", Float64.infinity)
|
||||
expectParse("1.79769313486232e+308", Float64.infinity)
|
||||
expectParse("1.79769313487e+308", Float64.infinity)
|
||||
expectParse("1.79769314e+308", Float64.infinity)
|
||||
expectParse("1.7977e+308", Float64.infinity)
|
||||
expectParse("1.798e+308", Float64.infinity)
|
||||
expectParse("1.8e+308", Float64.infinity)
|
||||
expectParse("2e+308", Float64.infinity)
|
||||
expectParse("1e309", Float64.infinity)
|
||||
expectParse("1e9999999999999999999999999999999999", Float64.infinity)
|
||||
expectParse("999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.999999999999999999999999999", Float64.infinity)
|
||||
}
|
||||
|
||||
@_extern(c, "_swift_stdlib_strtod_clocale")
|
||||
func _swift_stdlib_strtod_clocale(
|
||||
_: Optional<UnsafePointer<CChar>>,
|
||||
_: Optional<UnsafeMutablePointer<Double>>
|
||||
) -> Optional<UnsafePointer<CChar>>
|
||||
|
||||
func viaLegacy(_ text: String) -> Double? {
|
||||
return text.withCString { strptr -> Double? in
|
||||
var result = Double()
|
||||
let succeeded = withUnsafeMutablePointer(to: &result) { dptr in
|
||||
let endptr = _swift_stdlib_strtod_clocale(strptr, dptr)
|
||||
return endptr == strptr + text.utf8.count
|
||||
}
|
||||
if succeeded {
|
||||
return Double?.some(result)
|
||||
} else {
|
||||
return Double?.none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tests.test("Legacy ABI") {
|
||||
expectEqual(viaLegacy("1.0"), 1.0 as Double)
|
||||
expectEqual(viaLegacy("1.7976931348623157e+308"), Double.greatestFiniteMagnitude)
|
||||
}
|
||||
|
||||
/*
|
||||
// Checking 100 million random doubles takes only a fraction of a second on a
|
||||
// release build, but is _PAINFULLY SLOW_ in debug builds, so only enable this
|
||||
// locally!
|
||||
tests.test("Random Float64") {
|
||||
let blocks = 100_000
|
||||
let blocksize = 1_000
|
||||
for _ in 0..<blocks {
|
||||
var raw = UInt64.random(in: 0...UInt64.max)
|
||||
for _ in 0..<blocksize {
|
||||
raw &+= 1
|
||||
let d = Float64(bitPattern: raw)
|
||||
expectRoundTrip(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
runAllTests()
|
||||
@@ -8,6 +8,10 @@
|
||||
// rdar://77087867
|
||||
// UNSUPPORTED: CPU=arm64_32 && OS=watchos
|
||||
|
||||
// Float printing and parsing on embedded are new features, not
|
||||
// yet fully supported on WASM
|
||||
// UNSUPPORTED: CPU=wasm32
|
||||
|
||||
// rdar://104232602
|
||||
// UNSUPPORTED: CPU=x86_64 && (DARWIN_SIMULATOR=ios || DARWIN_SIMULATOR=watchos || DARWIN_SIMULATOR=tvos)
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
// rdar://77087867
|
||||
// UNSUPPORTED: CPU=arm64_32 && OS=watchos
|
||||
|
||||
// Float printing and parsing on embedded are new features, not
|
||||
// yet fully supported on WASM
|
||||
// UNSUPPORTED: CPU=wasm32
|
||||
|
||||
// rdar://104232602
|
||||
// UNSUPPORTED: CPU=x86_64 && (DARWIN_SIMULATOR=ios || DARWIN_SIMULATOR=watchos || DARWIN_SIMULATOR=tvos)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user