往返文本和返回

时间:2009-12-12 16:48:15

标签: c cocoa printf nsnumber

使用Cocoa或basic c,如何将double转换为字符串表示形式,然后将其转换回完全相同的double。可读性并不重要,只有准确性。 例如,这是否有效:
    双a,b;
    a =某个值;
    b = [[[NSNumber numberWithDouble:a] stringValue] doubleValue];

6 个答案:

答案 0 :(得分: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);

以下原始答案:


大多数双字符串转换无法保证。您可以尝试将double打印到非常高的精度(double可以存储大约16位十进制数字),但浮点错误的可能性仍然很小:

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

这将达到非常高的精度,但它仍然可能有几个ULP(最后一个单位)的错误。

你在做什么需要你在使用字符串时产生100%完全可重现的值?我强烈建议你重新考虑你想做什么。如果你真的需要一个精确的转换,只需将double存储为二进制的8个字节。如果需要以可打印格式存储,请将二进制数据转换为十六进制(16字节)或64位。

答案 1 :(得分:1)

正常转换为字符串几乎不可避免地会失去准确性。如果它必须是一个字符串,你可能想把它的地址放到一个指向char的指针,然后用十六进制写出各个字节。重新读取它们是类似的 - 从十六进制转换为一个字节,然后以正确的顺序将字节放入内存。

答案 2 :(得分:1)

要在Cocoa中创建和使用“普通”(即十进制)字符串表示,请使用NSString's stringWithFormat: methoddouble生成字符串,并使用NSScanner's scanDouble: method将字符串转换为double

但是,正如Jerry Coffin所说,这可能会失去精确度。写出原始字节将在同一台机器上运行,但它不是一种与架构无关的方法,因此如果数据从一台机器转到另一台机器,它将产生问题。

更好的方法是将值包装在NSNumber实例中。这是一个有效的属性列表对象。如果使用NSPropertyListSerialization类将其编码为二进制plist,则应保留所有精度对其他计算机可读。

答案 3 :(得分:1)

一种方法是简单地将字符串表示生成为比浮点类型可以处理的更有效的数字;例如17个十进制数字足以准确表示IEEE-754双精度数。这将保证您获得相同的数值(如果您的运行时库的转换例程已正确实现),但其缺点通常是为您提供比必要更多的数字。

Python 3.1最近添加了一种算法,可以将任何浮点值转换为最短的字符串,保证回滚到相同的值;如果您有兴趣,可以查看the implementation。不过,它非常毛茸茸。

答案 4 :(得分:0)

由于嵌入了零,您无法安全地将双精度复制到“字符串”。假设你的双重内容被保存在内存中0x43 0x63 0x00 0x00 0x00 0x00 0x00 0x01 ... C string是“抄送”,最后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;
}

答案 5 :(得分:0)

没有办法可移植。

上面的解决方案 - 将双重重新解释为原始位并打印它们 - 是一种不错的方法;在相关的说明中,如果您需要更多的可读性,可以从指数中分割尾数并以十进制形式打印。但是,即使这些也不能保证打印双精度到完美精度,例如,在x86架构上,编译器会经常优化浮点代码以使用128位SSE寄存器;上面的解决方案迫使它进入内存,它会丢失64位信息。