2009-12-12 73 views
0

使用Cocoa或基本c,如何將double轉換爲字符串表示形式,然後將其轉換回完全相同的double。可讀性並不重要,只有準確性。 例如,這項工作:
double a,b;
a =一些值;
b = [[[NSNumber numberWithDouble:a] stringValue] doubleValue];往返雙向文本並返回

+0

什麼是「basic c」? :-) – pmg 2009-12-12 17:00:18

回答

7

[編輯]其實有一種方法可以在C99中做到這一點 - 使用%a格式說明符。這會在十六進制科學記數法中打印出一個雙精度值,所以不會損失精度。結果是確切的。例如:

double d1 = 3.14159; 
char str[64]; 
sprintf(str, "%a", d1); 
// str is now something like "0x1.921f9f01b866ep+1" 
... 
double d2; 
sscanf(str, "%la", &d2); 
assert(d2 == d1); 

原文如下答案:


大多數雙到字符串的轉換不能保證。您可以嘗試打印出一倍,達到非常高的精度(雙能存儲約16位十進制數字),但仍然有浮點錯誤的可能性很小:

// Not guaranteed to work: 
double d = ...; 
char str[64]; 
sprintf(str, "%.30g", d); 
... 
double d2; 
sscanf(str, "%g", &d2); 

這將工作到一個很高的程度的精度,但它仍然可能有一些ULP(最後一個單位)的錯誤。

你在做什麼,需要你在使用字符串時產生100%完全可重現的值?我強烈建議你重新考慮你想要做的事情。如果你真的需要一個確切的轉換,只需要將二進制的8字節存儲爲二進制。如果需要以可打印的格式存儲,請將二進制數據轉換爲十六進制(16字節)或基本64.

+0

對於C99的%a,+1。 – 2009-12-12 20:14:52

+1

謝謝,使用%a似乎做了這項工作。使用可可我設法拉下列: double a = pi; double b; b = [[NSScanner scannerWithString:[NSString stringWithFormat:@「%a」,a]] scanHexDouble:&b] a == b – 2009-12-12 22:19:53

1

正常轉換爲字符串幾乎不可避免地會失去準確性。如果它必須是一個字符串,你可能需要將它的地址放入一個char指針,然後用十六進制寫出單個字節。讀回它們是相似的 - 從十六進制轉換爲一個字節,然後按照正確的順序將這些字節存入內存。

0

由於嵌入的零,您無法安全地將double複製到「字符串」。假設你的雙精度被保存在內存中作爲0x43 0x63 0x00 0x00 0x00 0x00 0x00 0x01 ...作爲C string即「Cc」,最後的0x01是「丟失」。

您可以將double轉換爲unsigned char array和背部

double val = 42.42; 
unsigned char val_char[sizeof (double)]; 

/* ... */ 

/* copy double to unsigned char array */ 
memcpy(val_char, &val, sizeof val); 

/* ... */ 

/* copy unsigned char array to double */ 
memcpy(&val, val_char, sizeof val); 

要打印unsigned char array,例如

printf("0x%02x", val_char[0]); 
for (k = 1; k < sizeof (double); k++) { 
    printf(" 0x%02x", val_char[k]); 
} 
puts(""); 

,並閱讀

fgets(buf, sizeof buf, stdin); 
for (k = 0; k < sizeof (double); k++) { 
    long tmp; 
    char *buf_end; 
    tmp = strtol(buf, buf_end, 0); 
    buf = buf_end + 1; 
    char_val[k] = tmp; 
} 
0

沒有辦法做到可移植。上面的解決方案 - 重新解釋雙原始位並打印這些 - 是一種體面的方式來做到這一點;在相關說明中,如果您需要更多的可讀性,您可以將指數的尾數分開並以十進制打印。但是,即使這些也不能保證打印雙精度到完美的精度,例如,在x86架構上,編譯器會經常優化浮點代碼以使用128位SSE寄存器;上面的解決方案強迫它進入內存,它將丟失64位信息。

1

要創建和可可使用「正常」(即,十進制)的字符串表示,使用NSString's stringWithFormat: methoddouble產生一個字符串,以及NSScanner's scanDouble: method到字符串轉換爲double。但是,正如Jerry Coffin所說,這可能會失去精確性。寫出原始字節將在同一臺機器上工作,但它不是獨立於體系結構的方式,所以如果數據從一臺機器轉移到另一臺機器上,會產生問題。

更好的方法是將值包裝在NSNumber實例中。這是一個有效的屬性列表對象。如果使用NSPropertyListSerialization類將其編碼爲二進制plist,則應該保持所有精度對其他機器可讀。

1

執行此操作的一種方法是簡單地將字符串表示生成爲比浮點類型可處理的更重要的數字;例如17位十進制數字足以準確表示IEEE-754雙精度型。這將保證你得到相同的數字值(如果你的運行時庫的轉換例程被正確實現),但是它的缺點是經常給你更多的數字。

Python 3.1最近添加了一種算法,可以將任何浮點值轉換爲最短字符串,以保證回到相同的值;如果你有興趣,你可以查看the implementation。雖然它很毛茸茸。