2009-11-03 38 views
7

Làm mới trên floating points (cũng PDF), IEEE-754 và tham gia in this discussion on floating point rounding when converting to strings, đưa tôi đến tinker: làm cách nào để có được giá trị tối đa và tối thiểu cho số dấu chấm động có biểu diễn nhị phân bằng nhau.Tìm min/max của float/double có cùng biểu diễn nội bộ

Tuyên bố từ chối: đối với thảo luận này, tôi thích gắn với điểm nổi 32 bit và 64 bit như được mô tả bởi IEEE-754. Tôi không quan tâm đến điểm nổi mở rộng (80 bit) hoặc quad (128 bit IEEE-754-2008) hoặc bất kỳ tiêu chuẩn nào khác (IEEE-854).

Bối cảnh: Máy tính không tốt đại diện cho 0.1 trong biểu diễn nhị phân. Trong C#, một phao đại diện cho số này là 3DCCCCCD trong nội bộ (C# sử dụng vòng-đến-gần nhất) và một đôi là 3FB999999999999A. Các mẫu bit giống nhau được sử dụng cho số thập phân 0.100000005 (phao) và 0.1000000000000000124 (gấp đôi), nhưng không được sử dụng cho 0.1000000000000000144 (đôi).

Để thuận tiện, mã C# dưới đây đưa ra những tuyên bố nội bộ:

string GetHex(float f) 
{ 
    return BitConverter.ToUInt32(BitConverter.GetBytes(f), 0).ToString("X"); 
} 

string GetHex(double d) 
{ 
    return BitConverter.ToUInt64(BitConverter.GetBytes(d), 0).ToString("X"); 
} 

// float 
Console.WriteLine(GetHex(0.1F)); 

// double 
Console.WriteLine(GetHex(0.1)); 

Trong trường hợp của 0.1, có số thập phân không thấp mà được thể hiện với các mẫu bit cùng, bất kỳ 0.99...99 sẽ mang lại một khác nhau biểu diễn bit (ví dụ: float cho 0.999999937 sản lượng 3F7FFFFF nội bộ).

Câu hỏi của tôi rất đơn giản: làm cách nào để tìm giá trị thập phân thấp nhất và cao nhất cho một float (hoặc double) được lưu trữ trong cùng một biểu diễn nhị phân.

Tại sao: (Tôi biết bạn sẽ hỏi) để tìm lỗi trong làm tròn trong .NET khi chuyển đổi thành chuỗi và khi nó chuyển đổi từ chuỗi, để tìm giá trị chính xác nội bộ và để hiểu làm tròn lỗi tốt hơn.

Đoán của tôi giống như: lấy phần định trị, loại bỏ phần còn lại, lấy giá trị chính xác, lấy giá trị (mantissa-bit) cao hơn và tính giá trị trung bình: bất kỳ thứ gì dưới đây sẽ mang lại cùng một mẫu bit. Vấn đề chính của tôi là: làm thế nào để có được phần phân số như số nguyên (bit thao tác nó không phải là tài sản mạnh nhất của tôi). Jon Skeet's DoubleConverter lớp học có thể hữu ích.

Trả lời

6

Một cách để có được câu hỏi của bạn là để tìm ra kích thước của một ULP, hoặc U nit trong L ast P ren, số dấu chấm động của bạn. Đơn giản hóa một chút, đây là khoảng cách giữa một số dấu phẩy động đã cho và số lớn hơn tiếp theo. Một lần nữa, đơn giản hóa một chút, cho một giá trị dấu phẩy động x, bất kỳ chuỗi thập phân nào có giá trị nằm trong khoảng (x - 1/2 ulp) và (x + 1/2 ulp) sẽ được làm tròn thành x khi được chuyển thành nổi giá trị điểm. Bí quyết là (x +/- 1/2 ulp) không phải là một số dấu phẩy động, vì vậy việc tính giá trị của nó thực sự yêu cầu bạn sử dụng loại dấu phẩy động rộng hơn (nếu có) hoặc một chiều rộng tùy ý lớn thập phân hoặc loại tương tự để thực hiện tính toán.

Bạn tìm kích thước của ulp bằng cách nào?Một cách tương đối dễ dàng là khoảng những gì bạn đề nghị, được viết ở đây là C-ish giả vì tôi không biết C#:

float absX = absoluteValue(x); 
uint32_t bitPattern = getRepresentationOfFloat(absx); 
bitPattern++; 
float nextFloatNumber = getFloatFromRepresentation(bitPattern); 
float ulpOfX = (nextFloatNumber - absX); 

này hoạt động vì thêm một đến các mẫu bit của x chính xác tương ứng với thêm một ULP để giá trị của x. Không có dấu chấm động nào xảy ra trong phép trừ vì các giá trị liên quan rất gần (đặc biệt, có một định lý về số học dấu chấm động ieee-754 nếu hai số x và y thỏa mãn y/2 < = x < = 2y, sau đó x - y được tính toán chính xác). Thông báo duy nhất ở đây là:

  1. nếu x là số điểm thả nổi hữu hạn lớn nhất, điều này sẽ không hoạt động (nó sẽ trả về inf, rõ ràng là sai).
  2. nếu nền tảng của bạn không hỗ trợ chính xác dòng chảy dần dần (nói thiết bị nhúng chạy ở chế độ từ tuôn sang không), điều này sẽ không hoạt động đối với các giá trị rất nhỏ của x.

Có vẻ như bạn không có khả năng thuộc một trong các trường hợp đó, vì vậy điều này sẽ chỉ hoạt động tốt cho mục đích của bạn.

Bây giờ bạn đã biết ulp của x là gì, bạn có thể tìm thấy khoảng thời gian của các giá trị làm tròn thành x. Bạn có thể tính toán chính xác ulp (x)/2 trong dấu phẩy động, bởi vì phân chia dấu phẩy động bằng 2 là chính xác (một lần nữa, chặn luồng). Sau đó, bạn chỉ cần tính giá trị của x +/- ulp (x)/2 loại dấu phẩy động lớn hơn phù hợp (double sẽ hoạt động nếu bạn quan tâm đến float) hoặc trong một loại Big Decimal và bạn có khoảng thời gian của mình.

Tôi đã thực hiện một vài giả định đơn giản hóa thông qua giải thích này. Nếu bạn cần điều này để thực sự được viết chính xác, hãy để lại nhận xét và tôi sẽ mở rộng trên các phần có chút mờ nhạt khi tôi có cơ hội.


Một khác lưu ý tuyên bố như sau trong câu hỏi của bạn:

Trong trường hợp 0.1, không có thấp hơn số thập phân được đại diện với các mẫu bit cùng

không chính xác. Bạn chỉ tình cờ nhìn vào các giá trị sai (0.999999 ... thay vì 0.099999 ... - một lỗi đánh máy dễ dàng).

+0

Câu trả lời hay, có vẻ như thông tin tôi đang tìm kiếm. Tôi sẽ cố gắng làm việc trong C# và quay lại đây nếu tôi cần thêm trợ giúp về các mẩu tin. Tôi nhận thấy bạn đã làm việc với đội ngũ IEEE-754 để xây dựng tiêu chuẩn? Tôi rất vinh dự :). Và bạn rất đúng về lỗi đánh máy đó! Tôi đã rất ngạc nhiên rằng tôi không thể tìm thấy một giá trị thấp hơn, nhưng tôi đã lấy nó cho các cấp và viết nó xuống, lỗi và tất cả, lol! – Abel

Các vấn đề liên quan