Có bao nhiêu số đôi một từ 0,0 đến 1,0?


94

Đây là điều tôi đã nghĩ trong nhiều năm, nhưng tôi chưa bao giờ dành thời gian để hỏi trước đây.

Nhiều trình tạo số ngẫu nhiên (giả) tạo ra một số ngẫu nhiên trong khoảng từ 0,0 đến 1,0. Về mặt toán học, có vô hạn số trong phạm vi này, nhưng doublelà một số dấu phẩy động, và do đó có độ chính xác hữu hạn.

Vì vậy, các câu hỏi là:

  1. Có bao nhiêu doublesố từ 0,0 đến 1,0?
  2. Có bao nhiêu số từ 1 đến 2? Giữa 100 và 101? Từ 10 ^ 100 đến 10 ^ 100 + 1?

Lưu ý: nếu nó tạo ra sự khác biệt, tôi doubleđặc biệt quan tâm đến định nghĩa của Java .

Câu trả lời:


68

Java doubles ở định dạng IEEE-754 , do đó chúng có phân số 52 bit; giữa hai lũy thừa liền kề bất kỳ của hai (bao gồm một và không bao gồm doublelũy thừa tiếp theo), do đó sẽ có từ 2 đến lũy thừa thứ 52 khác nhau (tức là 4503599627370496 trong số đó). Ví dụ: đó là số lượng phân biệt doublegiữa 0,5 được bao gồm và 1,0 bị loại trừ và chính xác là nhiều số cũng nằm giữa 1,0 được bao gồm và 2,0 bị loại trừ, v.v.

Đếm doublestừ 0,0 đến 1,0 khó hơn làm như vậy giữa các lũy thừa của hai, vì có nhiều lũy thừa của hai được bao gồm trong phạm vi đó, và, người ta cũng vướng vào những vấn đề hóc búa về các số không chuẩn hóa. 10 trong số 11 bit của số mũ bao hàm phạm vi được đề cập, vì vậy, bao gồm cả các số không chuẩn hóa (và tôi nghĩ là một vài loại NaN), bạn sẽ có 1024 lần số doubles nằm giữa lũy thừa của hai - 2**62dù sao thì tổng số cũng không nhiều hơn . Không bao gồm & c không chuẩn hóa, tôi tin rằng số lượng sẽ là 1023 lần 2**52.

Đối với một phạm vi tùy ý như "100 đến 100,1" thì càng khó hơn vì giới hạn trên không thể được biểu diễn chính xác dưới dạng a double(không phải là bội số chính xác của bất kỳ lũy thừa nào của hai). Như một phép gần đúng tiện dụng, vì tiến trình giữa các lũy thừa của hai là tuyến tính, bạn có thể nói rằng khoảng nói trên là 0.1 / 64khoảng giữa các lũy thừa xung quanh của hai (64 và 128), vì vậy bạn mong đợi khoảng

(0.1 / 64) * 2**52

phân biệt doubles - nói đến 7036874417766.4004... cho hoặc lấy một hoặc hai ;-).


@Alex: chỉ cần lưu ý, khi tôi viết 100 đến 100,1 tôi đã viết sai. Ý tôi là 100 đến 101. Về cơ bản, giữa N và N + 1 cho N. tùy ý
polygenelubricants

4
@Alex: vậy hãy để tôi nói thẳng điều này: không thể có nhiều hơn 2**64giá trị kép có thể có (vì nó là loại 64 bit), và rõ ràng là một tỷ lệ LỚN của những giá trị đó nằm giữa 0..1?
polygenelubricants

9
@polygene, yes và yes - cụ thể, khoảng một phần tư giá trị có thể có (đối với bất kỳ biểu diễn dấu phẩy động "bình thường" nào của bất kỳ độ dài cơ số và lũy thừa so với phân số) nằm trong khoảng từ 0,0 đến 1,0 (một phần tư khác từ 1,0 đến vô cùng, và còn lại nằm trên nửa âm của trục thực). Về cơ bản, một nửa giá trị của số mũ (với độ lệch bình thường, nửa trong phạm vi của nó) đại diện cho lũy thừa âm của cơ số, do đó các số <1,0.
Alex Martelli

8
@polygenelubricants: đối với nhiều ứng dụng, phạm vi từ 0 đến 1 quan trọng và thú vị hơn nhiều so với phạm vi từ 100 đến 101, đó là lý do tại sao nó nhận được phần lớn giá trị hơn. Ví dụ, trong vật lý, bạn thường phải đối mặt với các giá trị nhỏ đến mức nực cười như hằng số trọng trường của Newton ở 6,67e-11. Có độ chính xác tốt sẽ hữu ích hơn từ 100 đến 101. Đọc float-point-gui.de để biết thêm thông tin.
Michael Borgwardt

1
Bạn cũng có thể chia tỷ lệ bất kỳ số nào trong khoảng từ 0,0 đến 1,0, theo dõi tỷ lệ riêng biệt, mang lại ít lỗi hơn trong tính toán. Thật tuyệt khi toàn bộ dãy số có thể được ánh xạ giữa hai số!
codekaizen

42

Mọi doublegiá trị có biểu diễn nằm giữa 0x00000000000000000x3ff0000000000000nằm trong khoảng [0,0, 1,0]. Đó là (2 ^ 62 - 2 ^ 52) các giá trị riêng biệt (cộng hoặc trừ một vài giá trị tùy thuộc vào việc bạn có đếm các điểm cuối hay không).

Khoảng [1,0, 2,0] tương ứng với các đại diện giữa 0x3ff00000000000000x400000000000000; đó là 2 ^ 52 giá trị khác biệt.

Khoảng [100.0, 101.0] tương ứng với các đại diện giữa 0x40590000000000000x4059400000000000 ; đó là 2 ^ 46 giá trị khác biệt.

Không có bộ đôi nào từ 10 ^ 100 đến 10 ^ 100 + 1 . Không một trong những con số đó có thể biểu diễn được với độ chính xác gấp đôi và không có số nhân đôi nào nằm giữa chúng. Hai số chính xác kép gần nhất là:

99999999999999982163600188718701095...

10000000000000000159028911097599180...

+1, để có câu trả lời chính xác được hỗ trợ tốt. (Nếu bạn kén chọn trong việc đếm các điểm cuối, hãy nhớ rằng +0.0 và -0.0 có các đại diện riêng biệt.)
Jim Lewis

1
+1, một kết thúc xoắn như vậy! Cảm giác như tôi đang đọc một kịch bản M. Night Shyamalan!
polygenelubricants

7

Những người khác đã giải thích rằng có khoảng 2 ^ 62 nhân đôi trong phạm vi [0,0, 1,0].
(Không thực sự đáng ngạc nhiên: có gần 2 ^ 64 bộ đôi hữu hạn riêng biệt; trong số đó, một nửa là số dương và khoảng một nửa số đó là <1,0.)

Nhưng bạn đề cập đến trình tạo số ngẫu nhiên: lưu ý rằng trình tạo số ngẫu nhiên tạo ra các số từ 0,0 đến 1,0 nói chung không thể tạo ra tất cả các số này; thông thường nó sẽ chỉ tạo ra các số có dạng n / 2 ^ 53 với n là số nguyên (ví dụ: xem tài liệu Java cho nextDouble ). Vì vậy, thường chỉ có khoảng 2 ^ 53 (+/- 1, tùy thuộc vào điểm cuối nào được bao gồm) các giá trị có thể cho random()đầu ra. Điều này có nghĩa là hầu hết các nhân đôi trong [0.0, 1.0] sẽ không bao giờ được tạo.


3

Bài báo Toán học mới của Java, Phần 2: Các số dấu phẩy động của IBM cung cấp đoạn mã sau để giải quyết vấn đề này (trong số float, nhưng tôi nghi ngờ nó cũng hoạt động đối với số kép):

public class FloatCounter {

    public static void main(String[] args) {
        float x = 1.0F;
        int numFloats = 0;
        while (x <= 2.0) {
            numFloats++;
            System.out.println(x);
            x = Math.nextUp(x);
        }
        System.out.println(numFloats);
    }
}

Họ có nhận xét này về nó:

Hóa ra có chính xác 8.388.609 lượt nổi từ 1.0 đến 2.0; lớn nhưng hầu như không đếm được số thực tồn tại trong phạm vi này. Các số liên tiếp cách nhau khoảng 0,0000001. Khoảng cách này được gọi là ULP cho đơn vị có độ chính xác thấp nhất hoặc đơn vị ở vị trí cuối cùng.


Đúng, nhưng đó là vì float, not double - floats có giá trị phân số là 23 bit, vì vậy 2**23 -> 8388608các giá trị khác nhau giữa các lũy thừa liền kề của hai (phần "bao gồm" tất nhiên có nghĩa là bạn phải đếm thêm một, lũy thừa tiếp theo của hai). doubles có phân số 52-bit!
Alex Martelli

1
@ Alex: Tôi đoán tôi sẽ phải rời khỏi chương trình (sửa đổi cho đôi) chạy cho đến khi kết thúc của vũ trụ hoặc lâu hơn trước khi tôi có thể nhận được kết quả ... :(
Đánh dấu Rushakoff

1
Tôi thấy ngớ ngẩn; Tôi chỉ viết doubletương đương và nghĩ rằng "Này, tôi sẽ trả lời câu hỏi của riêng tôi trong khoảng 5 phút ..."
polygenelubricants

1
@polygene: Đây cảm thấy như một vấn đề Dự án Euler nơi cách tiếp cận rõ ràng là không khả thi để tính toán, nhưng phải có một số công thức rực rỡ đơn giản để giải quyết đối với trường hợp tùy ý ...
Đánh dấu Rushakoff

2
có thể không với một siêu máy tính tăng áp thực sự: trên một chiếc máy chỉ mất một nano giây để chạy vòng lặp bên trong, việc đếm với doublecác lũy thừa liền kề của hai sẽ mất khoảng 52 ngày ( printlntất nhiên sẽ rất khó chạy nhanh như vậy cho dù thế nào, vì vậy giả sử rằng một câu lệnh biến mất ;-). Tôi nghĩ rằng có thể mất một năm hoặc ít hơn trên một cỗ máy mạnh mẽ nhưng thực tế ;-).
Alex Martelli

2
  1. 2 ^ 53 - kích thước của dấu và / phần định trị của một số dấu phẩy động 64 bit bao gồm cả bit ẩn.
  2. Đại khái là có, vì sifnificand được cố định nhưng số mũ thay đổi.

Xem bài viết wikipedia để biết thêm thông tin.


Câu trả lời của bạn cho 2 mâu thuẫn với cách tôi hiểu hoạt động của FP.
polygenelubricants

Tôi nghĩ 1là sai vì các bit ẩn luôn là một - do đó, 2^52, không 2^53 phân biệt giá trị (giữa quyền hạn liền kề hai, một bao gồm và người tiếp theo bị loại - không ! Giữa 0.0 và 1.0).
Alex Martelli

1

Đôi Java là một số IEEE 754 binary64.

Điều này có nghĩa là chúng ta cần xem xét:

  1. Mantissa là 52 bit
  2. Số mũ là số 11 bit với 1023 thiên vị (tức là với 1023 được thêm vào nó)
  3. Nếu số mũ đều là 0 và phần định trị khác 0 thì số đó được cho là không chuẩn hóa

Về cơ bản, điều này có nghĩa là có tổng số 2 ^ 62-2 ^ 52 + 1 trong số các biểu diễn kép có thể có mà theo tiêu chuẩn là từ 0 đến 1. Lưu ý rằng 2 ^ 52 + 1 là để loại bỏ các trường hợp không chuẩn hóa những con số.

Hãy nhớ rằng nếu phần định trị là số dương nhưng số mũ là số âm thì số dương nhưng nhỏ hơn 1 :-)

Đối với các số khác thì khó hơn một chút vì các số nguyên cạnh có thể không thể biểu diễn một cách chính xác trong biểu diễn IEEE 754 và bởi vì có các bit khác được sử dụng trong số mũ để có thể biểu diễn các số, vì vậy số càng lớn càng thấp các giá trị khác nhau.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.