2012-05-02 36 views
9

Python có cung cấp một hàm để lấy giá trị dấu phẩy động mà kết quả từ việc tăng bit ít quan trọng nhất của giá trị dấu phẩy động hiện có không?Làm thế nào để bạn nhận được giá trị tiếp theo trong chuỗi dấu chấm động?

Tôi đang tìm một cái gì đó tương tự như hàm std::nextafter đã được thêm vào C++ 11.

+0

Tôi tin rằng không, nhưng chỉ cố gắng xem cách 'std :: nextafter' được triển khai và có thể chúng ta có thể tìm ra thứ gì đó tương đương. – Abhijit

+0

Giải pháp dựa trên cython của tôi sẽ hoạt động trên các cửa sổ (xem bạn là một anh chàng cửa sổ trong tiểu sử của bạn). Bạn có thể cài đặt cython từ pip. Yêu cầu gcc/g ++. Bạn có thể phải đặt các đường dẫn thư viện, library_dirs và include_dirs trong setup.py, chúng là các đường dẫn tệp tiêu chuẩn mà bạn sẽ chuyển tới trình biên dịch của bạn để xây dựng một cái gì đó sử dụng cmath. Không chắc chắn những gì những con đường sẽ trông giống như trên cửa sổ hoặc thậm chí nếu họ sẽ là cần thiết (có lẽ không), nhưng không có lý do nó không nên làm việc. – Endophage

+0

Không, không. Cách dễ nhất để giả mạo nó là sử dụng mô-đun struct để chuyển đổi thành một int 8 byte, thêm một vào int và chuyển đổi ngược lại. Điều đó làm việc tốt cho các số dương và cần một số tinh chỉnh cho các số âm. –

Trả lời

1

Khám phá http://docs.python.org/library/stdtypes.html#float.hex

Hãy thử thực hiện điều này không biết nhiều về sau.

Đầu tiên, chúng ta cần phải trích xuất các phần hex và số mũ từ chuỗi hex:

def extract_parts(hex_val): 
    if not hex_val.startswith('0x1.'): 
     return None 
    relevant_chars = hex_val[4:] 
    if not len(relevant_chars) > 14 and relevant_chars[13] == 'p': 
     return None 
    hex_portion = int(relevant_chars[:13], 16) 
    if relevant_chars[14] == '+': 
     p_val = int(relevant_chars[15:]) 
    elif relevant_chars[14] == '-': 
     p_val = -int(relevant_chars[15:]) 
    else: 
     return None 
    return (hex_portion, p_val) 

Sau đó, chúng ta cần một cách để tăng theo hướng tích cực hay tiêu cực (chúng tôi sẽ giả chuỗi hex có đã được chuyển đổi sang một số nguyên hex_portion):

def increment_hex(hex_portion, p_val, direction): 
    if hex_portion == 0 and direction == -1: 
     new_hex = 'f' * 13 
     p_val -= 1 
    elif hex_portion == int('f' * 13, 16) and direction == 1: 
     new_hex = '0' * 13 
     p_val += 1 
    else: 
     new_hex = hex(hex_portion + direction)[2:].rstrip('L').zfill(13) 

    if len(new_hex) != 13: 
     return None 
    return format_hex(new_hex, p_val) 

Chúng ta cần một hàm helper để định dạng một chuỗi có thể chấp nhận hex và số mũ, mà tôi sử dụng ở trên:

def format_hex(hex_as_str, p_val): 
    sign = '-' if p_val < 0 else '+' 
    return '0x1.%sp%s%d' % (hex_as_str, sign, p_val) 

Cuối cùng, để thực hiện nextafter:

def nextafter(float_val): 
    hex_equivalent = float_val.hex() 
    hex_portion, p_val = extract_parts(hex_equivalent) 

    direction = 1 
    new_hex_equiv = increment_hex(hex_portion, p_val, direction) 
    return float.fromhex(new_hex_equiv) 
+1

Huh? Làm thế nào là tương đương với 'std :: nextafter'? – phihag

+1

@phihag, nó không tương đương chút nào nhưng có thể là cơ sở để viết của riêng bạn. –

2

UPDATE:

Hóa ra đây là một câu hỏi trùng lặp (mà đi lên trong google kết quả # 2 cho việc tìm kiếm "C++ nextafter trăn"): Increment a python floating point value by the smallest possible amount

Câu trả lời được chấp nhận cung cấp một số giải pháp vững chắc.

ORIGINAL ĐÁP:

Chắc chắn đây không phải là giải pháp hoàn hảo nhưng sử dụng cython chỉ là một vài dòng sẽ cho phép bạn quấn ++ chức năng C hiện có và sử dụng nó trong Python. Tôi đã biên dịch mã dưới đây và nó hoạt động trên hộp ubuntu 11.10 của tôi.

Đầu tiên, một tập tin .pyx (tôi gọi tôi nextafter.pyx) định nghĩa giao diện của bạn vào thư mục C++:

cdef extern from "cmath": 
    float nextafter(float start, float to) 

def pynextafter(start, to): 
    cdef float float_start = float(start) 
    cdef float float_to = float(to) 
    result = nextafter(start, to) 
    return result 

Sau đó, một setup.py xác định làm thế nào để xây dựng phần mở rộng:

from distutils.core import setup 
from distutils.extension import Extension 
from Cython.Distutils import build_ext 

ext_modules=[ 
    Extension("nextafter", 
     ["nextafter.pyx"], 
     libraries=[], 
     library_dirs=[], 
     include_dirs=[], 
     language="c++", 
    ) 
] 

setup(
    name = "nextafter", 
    cmdclass = {"build_ext": build_ext}, 
    ext_modules = ext_modules 
) 

Hãy chắc chắn rằng những người đang ở trong cùng một thư mục sau đó xây dựng với python setup.py build_ext --inplace. Tôi hy vọng bạn có thể thấy làm thế nào bạn sẽ thêm các biến thể khác của nextafter để mở rộng (cho đôi, vv ...). Sau khi xây dựng, bạn nên có một nextafter.so. Cháy lên python trong cùng một thư mục (hoặc đặt nextafter.so trên đường dẫn của bạn một nơi nào đó) và bạn sẽ có thể gọi from nextafter import pynextafter.

Tận hưởng!

+0

Kỳ lạ là tôi có câu trả lời cho câu hỏi đó. –

+0

@MarkRansom umm ... lol? – Endophage

+0

Tôi nghĩ bạn cần thay thế 'float' bằng' double' ở mọi nơi trong tệp '.pyx' của bạn. (Hoặc sử dụng 'nextafterf' nếu bạn thực sự muốn sử dụng phao, nhưng kể từ khi Python nổi tương ứng với C++ tăng gấp đôi, phiên bản kép sẽ có ý nghĩa hơn.) –

13

Để trả lời phần đầu tiên của câu hỏi của bạn: không, Python không cung cấp chức năng này trực tiếp. Nhưng nó khá dễ dàng để viết một hàm Python thực hiện điều này, giả sử điểm nổi bật của IEEE 754.

Các định dạng dấu phẩy động nhị phân IEEE 754 được thiết kế khá khéo léo để di chuyển từ một số dấu phẩy động sang số 'tiếp theo' cũng đơn giản như tăng dần biểu diễn bit. Điều này làm việc cho bất kỳ số nào trong phạm vi [0, infinity), ngay trên các đường biên và số mũ con. Để tạo ra một phiên bản nextUp bao gồm phạm vi dấu phẩy động hoàn chỉnh, bạn cũng cần phải xử lý các số âm, số vô hạn, nans và một trường hợp đặc biệt liên quan đến số không âm. Dưới đây là phiên bản tuân thủ tiêu chuẩn của chức năng nextUp của IEEE 754 bằng Python. Nó bao gồm tất cả các trường hợp góc.

import math 
import struct 

def next_up(x): 
    # NaNs and positive infinity map to themselves. 
    if math.isnan(x) or (math.isinf(x) and x > 0): 
     return x 

    # 0.0 and -0.0 both map to the smallest +ve float. 
    if x == 0.0: 
     x = 0.0 

    n = struct.unpack('<q', struct.pack('<d', x))[0] 
    if n >= 0: 
     n += 1 
    else: 
     n -= 1 
    return struct.unpack('<d', struct.pack('<q', n))[0] 

Việc triển khai nextDownnextAfter thì trông như thế này. (Lưu ý rằng nextAfter không phải là một chức năng được chỉ định bởi IEEE 754, do đó, có một chút phỏng đoán về những gì sẽ xảy ra với các giá trị đặc biệt của IEEE. Tôi đang theo tiêu chuẩn Số thập phân của IBM mà lớp decimal.Decimal của Python dựa trên.)

def next_down(x): 
    return -next_up(-x) 

def next_after(x, y): 
    # If either argument is a NaN, return that argument. 
    # This matches the implementation in decimal.Decimal 
    if math.isnan(x): 
     return x 
    if math.isnan(y): 
     return y 

    if y == x: 
     return y 
    elif y > x: 
     return next_up(x) 
    else: 
     return next_down(x) 
Các vấn đề liên quan