2016-09-29 17 views
5

Đây là một mã đơn giản trong Xcode 7.3.1 sân chơi:nhanh chóng: vấn đề trong chuyển đổi chuỗi tăng gấp đôi

var str = "8.7" print(Double(str))

đầu ra ngạc nhiên khi: Optional(8.6999999999999993)

cũng

, Float(str) cung cấp: 8.69999981

Bất kỳ suy nghĩ hoặc lý do nào o n anh chàng này? Mọi tham chiếu đến điều này sẽ được đánh giá cao.

Ngoài ra, sau đó, tôi nên chuyển đổi "8,7" thành 8,7 thành Double (hoặc Float) như thế nào?

Sửa

trong nhanh chóng:

(str như NSString) .doubleValue trả 8,7

Bây giờ, đó là Ok. Nhưng câu hỏi của tôi, vẫn không nhận được câu trả lời hoàn chỉnh. Chúng tôi đã tìm thấy một giải pháp thay thế nhưng tại sao chúng ta không thể dựa vào Double ("8.7"). Xin vui lòng, cung cấp một cái nhìn sâu sắc hơn về điều này.

Chỉnh sửa 2

("6,9" như NSString) .doubleValue // in 6,9000000000000004

Vì vậy, câu hỏi mở ra một lần nữa.

+0

sử dụng in (Double (str)!) –

+4

Nếu câu hỏi của bạn là khoảng 8,7 so với 8,69999981 thì bạn nên đọc [Toán tử dấu chấm động bị hỏng?] (Http://stackoverflow.com/questions/588004/is-floating- point-math-broken) và [Mỗi nhà khoa học máy tính nên biết gì về số học dấu chấm động] (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). –

+0

"Định dạng dấu phẩy động kép chính xác là định dạng số máy tính chiếm 8 byte (64 bit) trong bộ nhớ máy tính và đại diện cho phạm vi rộng, năng động của các giá trị bằng cách sử dụng dấu phẩy động.", Do đó bạn phải chỉ định "độ chính xác" của giá trị (độ chính xác là số chữ số trong một số). –

Trả lời

9

Có hai vấn đề khác nhau ở đây. Đầu tiên - như đã đề cập trong các chú thích - số dấu phẩy động nhị phân không thể đại diện cho số 8.7 chính xác. Swift sử dụng các tiêu chuẩn IEEE 754 cho đại diện số dấu chấm động đơn và kép chính xác, và nếu bạn gán

let x = 8.7 

sau đó là số biểu diễn gần nhất được lưu trữ trong x, và đó là

8.699999999999999289457264239899814128875732421875 

Bạn có thể tìm thấy thêm thông tin về điều này trong số tuyệt vời Q & A Is floating point math broken?.


Vấn đề thứ hai là: Tại sao là số đôi khi in như "8,7" và đôi khi là "8,6999999999999993"?

let str = "8.7" 
print(Double(str)) // Optional(8.6999999999999993) 

let x = 8.7 
print(x) // 8.7 

Double("8.7") khác với 8.7?Có chính xác hơn mục còn lại không?

Để trả lời những câu hỏi này, chúng ta cần phải biết làm thế nào print() chức năng hoạt động:

  • Nếu cãi nhau phù hợp với CustomStringConvertible, chức năng in gọi bất động sản description của nó và in kết quả đến đầu ra tiêu chuẩn.
  • Nếu không, nếu đối số phù hợp với CustomDebugStringConvertible, , gọi hàm là debugDescription và in kết quả cho đầu ra tiêu chuẩn.
  • Nếu không, một số cơ chế khác sẽ được sử dụng. (Không phải nhập khẩu ở đây cho mục đích của chúng tôi.)

Loại Double phù hợp với CustomStringConvertible, do đó

let x = 8.7 
print(x) // 8.7 

sẽ cho kết quả tương tự như

let x = 8.7 
print(x.description) // 8.7 

Nhưng những gì xảy ra trong

let str = "8.7" 
print(Double(str)) // Optional(8.6999999999999993) 

Double(str) là một tùy chọn, và struct Optional không không phù hợp với CustomStringConvertible, nhưng để CustomDebugStringConvertible. Do đó, chức năng in gọi thuộc tính debugDescription của Optional, lần lượt gọi số debugDescription của số Double cơ bản. Do đó - ngoài việc là một tùy chọn - đầu ra số là giống như trong

let x = 8.7 
print(x.debugDescription) // 8.6999999999999993 

Nhưng sự khác biệt giữa descriptiondebugDescription cho các giá trị dấu chấm động là gì? Từ mã nguồn Swift, bạn có thể xem cả hai cuối cùng gọi hàm swift_floatingPointToString trong Stubs.cpp, với thông số Debug được đặt thành falsetrue, tương ứng. này điều khiển độ chính xác của số để chuyển đổi chuỗi:

int Precision = std::numeric_limits<T>::digits10; 
    if (Debug) { 
    Precision = std::numeric_limits<T>::max_digits10; 
    } 

Đối với ý nghĩa của những hằng số, xem http://en.cppreference.com/w/cpp/types/numeric_limits:

  • digits10 - số chữ số thập phân có thể được trình bày mà không cần thay đổi,
  • max_digits10 - số chữ số thập phân cần thiết để phân biệt tất cả các giá trị thuộc loại này.

Vì vậy, description tạo một chuỗi có ít chữ số thập phân hơn. Đó là chuỗi có thể được chuyển đổi thành Double và quay lại chuỗi cho số cùng một kết quả. debugDescription tạo chuỗi có nhiều chữ số thập phân hơn để bất kỳ hai giá trị điểm động nào khác nhau sẽ tạo ra một đầu ra khác nhau.


Tóm tắt:

  • Hầu hết các số thập phân không thể được đại diện chính xác như một nhị phân giá trị dấu chấm động.
  • Phương thức descriptiondebugDescription của các loại điểm nổi sử dụng độ chính xác khác để chuyển đổi thành chuỗi . Kết quả là,
  • in tùy chọn giá trị dấu phẩy động sử dụng độ chính xác khác cho chuyển đổi so với in giá trị không tùy chọn.

Vì vậy trong trường hợp của bạn, có thể bạn muốn unwrap các tùy chọn trước khi in nó:

let str = "8.7" 
if let d = Double(str) { 
    print(d) // 8.7 
} 

Để kiểm soát tốt hơn, sử dụng NSNumberFormatter hoặc định dạng in với định dạng %.<precision>f.

Tùy chọn khác có thể sử dụng (NS)DecimalNumber thay vì Double (ví dụ: đối với số tiền), xem ví dụ: Round Issue in swift.

+0

bạn có thể vui lòng, hãy xem chỉnh sửa cuối cùng trong câu hỏi. –

+1

@ v-i-s-h-a-l: '(" 6.9 "là NSString) .doubleValue' in' "6.9" 'trong thử nghiệm của tôi. Nhưng ngay cả khi nó không: Bạn không thể dựa vào một đầu ra nào đó, trừ khi bạn sử dụng một trình định dạng số. –

0

Bạn có thể sử dụng mã này có thể hữu ích.

print(str.doubleValue) 
1

Tôi sẽ sử dụng:

let doubleValue = NSNumberFormatter().numberFromString(str)?.doubleValue 
Các vấn đề liên quan