往返文本和返回文本

发布于 2024-08-14 17:23:39 字数 186 浏览 8 评论 0原文

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

Using Cocoa or basic c, how can I convert a double to a string representation and then convert it back to the exact same double. Readability is not important, only accuracy.
For example, will this work:
double a,b;
a=some value;
b=[[[NSNumber numberWithDouble:a] stringValue] doubleValue];

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

与君绝 2024-08-21 17:23:39

[编辑] 实际上,在 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。

[EDIT] Actually there is a way to do this in C99 -- use the %a format specifier. This prints out a double in hexadecimal scientific notation, so there is no loss of precision. The result is exact. Example:

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);

Original answer below:


Most double-to-string conversions can't guarantee that. You can try printing out the double to a very high precision (a double can store about 16 decimal digits), but there's still a small chance of floating-point error:

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

This will work to a very high degree of precision, but it still may have an error of a few ULPs (units in the last place).

What are you doing that requires you to produce a 100% exactly reproducible value while using a string? I strongly suggest you reconsider what you're trying to do. If your really need an exact conversion, just store the double as 8 bytes in binary. If it needs to be stored in a printable format, convert the binary data into hex (16 bytes) or base 64.

新一帅帅 2024-08-21 17:23:39

正常的字符串转换几乎不可避免地会损失准确性。如果它必须是一个字符串,您可能需要将其地址放入指向 char 的指针中,然后以十六进制写出各个字节。读回它们的方法类似——从十六进制转换为字节,然后以正确的顺序将字节放入内存中。

A normal conversion to a string will almost inevitably lose accuracy. If it has to be a string, you'll probably want to put its address into a pointer to char, then write out the individual bytes in hex. Reading them back in is similar -- convert from hex to a byte, then put the bytes into memory in the correct order.

网白 2024-08-21 17:23:39

要在 Cocoa 中创建和使用“正常”(即十进制)字符串表示形式,请使用 NSString 的 stringWithFormat: 方法生成字符串双,和NSScanner 的 scanDouble: 方法,将字符串转换为 double

但是,正如杰里·科芬所说,这可能会失去精度。写出原始字节可以在同一台机器上工作,但这不是一种独立于体系结构的方法,因此如果数据从一台机器传输到另一台机器,就会产生问题。

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

To create and use the “normal” (i.e., decimal) string representation in Cocoa, use NSString's stringWithFormat: method to produce a string from a double, and NSScanner's scanDouble: method to convert a string to a double.

But, as Jerry Coffin said, this may lose precision. Writing out raw bytes will work on the same machine, but it's not an architecture-independent way to do it, so it'll create problems if the data ever goes from one machine to a different one.

A better way is to wrap the value in an NSNumber instance. That's a valid property list object. If you encode it as a binary plist using the NSPropertyListSerialization class, that should preserve all precision and be readable to other machines.

追风人 2024-08-21 17:23:39

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

Python 3.1 最近添加了一种算法,可以将任何浮点值转换为保证四舍五入为相同值的最短字符串;如果您有兴趣,可以查看实现。不过,它毛茸茸的。

One way to do this is simply to generate the string representation to more significant digits than your floating-point type can handle; e.g. 17 decimal digits is sufficient to exactly represent an IEEE-754 double. This will guarantee that you get the same numeric value back (if your runtime library's conversion routines are correctly implemented) but has the downside of often giving you more digits than are necessary.

Python 3.1 recently added an algorithm that converts any floating-point value to the shortest string guaranteed to round back to the same value; you can check out the implementation if you're interested. It's pretty hairy, though.

我喜欢麦丽素 2024-08-21 17:23:39

由于嵌入了零,您无法安全地将双精度值复制到“字符串”。假设您的双精度数在内存中保存为 0x43 0x63 0x00 0x00 0x00 0x00 0x00 0x01 ... 作为 C 字符串,即“Cc”,最后一个 0x01< /code>“丢失”。

您可以将 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;
}

You cannot safely copy a double to a "string" because of embedded zeros. Suppose your double is held in memory as 0x43 0x63 0x00 0x00 0x00 0x00 0x00 0x01 ... as a C string that is "Cc", the last 0x01 is "lost".

You can convert a double to a unsigned char array and back

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);

To print the unsigned char array, for example

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

And to read

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;
}
筱果果 2024-08-21 17:23:39

没有办法做到便携。

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

There's no way to do it portably.

The solutions above -- reinterpreting the double as raw bits and printing those -- is one decent way to do it; on a related note, if you need more readability you can split the mantissa from the exponent and print both in decimal. Even these are not guaranteed to print the double to perfect precision, however: for instance, on x86 architecture the compiler will frequently optimize floating-point code to use the 128-bit SSE registers; the solutions above force it into memory where it will lose 64 bits of information.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文