[stdlib] Floating-point parsing

Swift SVN r25627
This commit is contained in:
Dave Abrahams
2015-02-28 00:12:30 +00:00
parent 8cf7b8b4ae
commit 367f308888
5 changed files with 104 additions and 3 deletions

View File

@@ -9,6 +9,7 @@
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftShims
%{
#
@@ -59,6 +60,8 @@ def cFuncSuffix(bits):
if bits == 80:
return 'l'
cFuncSuffix2 = {32: 'f', 64: 'd', 80: 'ld'}
def llvmIntrinsicSuffix(bits):
if bits == 32:
return 'f32'
@@ -200,6 +203,44 @@ extension ${Self} : Printable {
}
}
//===--- Parsing ----------------------------------------------------------===//
extension ${Self} {
/// Construct from an ASCII representation.
///
/// The result is `nil` if `text` contains non-ASCII text or
/// whitespace, or if it is not completely consumed by the POSIX
/// function `strto${cFuncSuffix2[bits]}`. Otherwise, the result is
/// the value returned by `strto${cFuncSuffix2[bits]}`. See the
/// `strto${cFuncSuffix2[bits]} (3)` man page for details of the
/// exact format accepted.
public init?(_ text: String) {
let u16 = text.utf16
// Any non-ASCII or whitespace? return nil.
if contains(u16, { $0 > 127 || isspace(Int32($0)) != 0 }) {
return nil
}
func parseNTBS(chars: UnsafePointer<CChar>) -> (${Self}, Int) {
% if bits == 80:
var result: Float80 = 0
let endPtr = withUnsafeMutablePointer(&result) {
_swift_strtold(chars, UnsafeMutablePointer($0))
}
% else:
var endPtr: UnsafeMutablePointer<CChar> = nil
let result = withUnsafeMutablePointer(&endPtr) {
strto${cFuncSuffix2[bits]}(chars, $0)
}
% end
return (result, endPtr == nil ? 0 : UnsafePointer(endPtr) - chars)
}
let (result, n) = text.withCString(parseNTBS)
if n == 0 || n != count(u16) { return nil }
self = result
}
}
% if bits in allIntBits:
// Not transparent because the compiler crashes in that case.
//@transparent

View File

@@ -311,3 +311,10 @@ extern "C" uint64_t swift_stdlib_atomicFetchAddUInt64(
return __c11_atomic_fetch_add(object, operand, memory_order_seq_cst);
}
// We can't return Float80, but we can receive a pointer to one, so
// switch the return type and the out parameter on strtold.
extern "C" const char *_swift_strtold(const char * nptr, void *outResult) {
char *endPtr;
*static_cast<long double*>(outResult) = std::strtold(nptr, &endPtr);
return endPtr;
}

View File

@@ -29,5 +29,10 @@ int strcmp(const char *s1, const char *s2);
int memcmp(const void *s1, const void *s2, __swift_size_t n);
int putchar(int c);
double strtod(const char *restrict nptr, char **restrict endptr);
float strtof(const char *restrict nptr, char **restrict endptr);
int isspace(int c);
#endif // SWIFT_STDLIB_SHIMS_DARWINSHIMS_H

View File

@@ -72,7 +72,10 @@ bool _swift_usesNativeSwiftReferenceCounting_class(const void *);
__swift_size_t _swift_class_getInstancePositiveExtentSize(const void *);
/// Return an NSString to be used as the Mirror summary of the object
void* _swift_objCMirrorSummary(const void * nsObject);
void *_swift_objCMirrorSummary(const void * nsObject);
/// Call strtold, changing arguments so we can operate on Float80
const char *_swift_strtold(const char *nptr, void *outResult);
#ifdef __cplusplus
}} // extern "C", namespace swift

View File

@@ -11,7 +11,7 @@
//===----------------------------------------------------------------------===//
// -*- swift -*-
// RUN: rm -rf %t ; mkdir -p %t
// RUN: %S/../../utils/gyb -Dtarget_ptrsize=%target-ptrsize %s -o %t/NumericParsing.swift
// RUN: %S/../../utils/gyb -DCMAKE_SIZEOF_VOID_P=%target-ptrsize %s -o %t/NumericParsing.swift
// RUN: %S/../../utils/line-directive %t/NumericParsing.swift -- %target-build-swift %t/NumericParsing.swift -o %t/a.out
// RUN: %S/../../utils/line-directive %t/NumericParsing.swift -- %target-run %t/a.out
//
@@ -19,7 +19,7 @@
%{
from SwiftIntTypes import *
word_bits = int(target_ptrsize)
word_bits = int(CMAKE_SIZEOF_VOID_P)
def maskOfWidth(n):
return (1 << n) - 1
@@ -47,6 +47,7 @@ number_of_values = 23
}%
import StdlibUnittest
import Darwin
var tests = TestSuite("NumericParsing")
@@ -115,4 +116,48 @@ tests.test("${Self}/radixTooHigh") {
% end
% for Self in 'Float', 'Double', 'Float80':
% if Self == 'Float80':
#if arch(i386) || arch(x86_64)
% end
tests.test("${Self}/Basics") {
setlocale(LC_ALL, "")
% if Self != 'Float80': # Inf/NaN are not defined for Float80
expectEqual(.infinity, ${Self}("Inf"))
expectEqual(-(.infinity), ${Self}("-Inf"))
expectEqual(toString(${Self}.NaN), toString(${Self}("NaN")!))
% end
expectEqual(-0.0, ${Self}("-0"))
expectEqual(-0.0, ${Self}("-0.0"))
expectEqual(0.0, ${Self}("0"))
expectEqual(0.0, ${Self}("0.0"))
expectEqual(64206, ${Self}("0xFACE")) // Yes, strtoXXX supports hex.
// Check that we can round-trip the string representation of 2^100,
// which requires an exponent to express...
let large = reduce(Repeat(count: 10, repeatedValue: 1024 as ${Self}), 1, *)
let largeText = toString(large)
expectEqual(largeText, toString(${Self}(largeText)!))
// ...ditto for its inverse.
let smallText = toString(1 / large)
expectEqual(smallText, toString(${Self}(smallText)!))
// Cases that should fail to parse
expectEmpty(${Self}("")) // EMPTY
expectEmpty(${Self}("0FACE")) // Hex characters without 0x
expectEmpty(${Self}("99x"))
expectEmpty(${Self}(" 0")) // Leading whitespace
expectEmpty(${Self}("0 ")) // Trailing whitespace
}
% if Self == 'Float80':
#endif
% end
% end
runAllTests()