可能这是一个非常基本的问题,但我真的很想知道到底发生了什么。
例如,如果我们在 c# 中执行以下操作:
object obj = "330.1500249000119";
var val = Convert.ToDouble(obj);
val 变为:330.15002490001189
问题是为什么最后的 9 被 89 取代?我们能阻止它以这种方式发生吗?这种精确度是否取决于当前文化?
可能这是一个非常基本的问题,但我真的很想知道到底发生了什么。
例如,如果我们在 c# 中执行以下操作:
object obj = "330.1500249000119";
var val = Convert.ToDouble(obj);
val 变为:330.15002490001189
问题是为什么最后的 9 被 89 取代?我们能阻止它以这种方式发生吗?这种精确度是否取决于当前文化?
这与文化无关。有些数字不能用以 2 为底的数字精确表示,就像在以 10 为底的 1/3rd 不能用 .3333333 精确表示一样
请注意,在您的特定情况下,您输入的数字超出了数据类型允许的范围: Double 可用的有效数字为 15-16(取决于范围),您的数字超出了该数字。
在这种情况下Double
,您可以使用 a而不是 a :Decimal
object obj = "330.1500249000119";
var val = Convert.ToDecimal(obj);
Adecimal
将保持精度。
object obj = "330.1500249000119";
var val = Convert.ToDecimal(obj);
您遇到的“问题”是浮点表示。
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
不,你无法阻止它发生。您正在解析的值具有数据类型可以表示的更多位数。
精度不依赖于文化。Adouble
始终具有相同的精度。
因此,如果您不希望它发生,那么就不要这样做。如果您不想要浮点数精度有限的影响,请不要使用浮点数。如果您改用定点数(十进制),它可以准确地表示该值。
CPU 代表 8 个字节的双精度数。其中分为 1 个符号位,11 位用于指数(“范围”)和 52 位用于尾数(“精度”)。您的范围和精度有限。
中的 C 常量DBL_DIG<float.h>
告诉您,这样的双精度数只能精确表示15 位数字,不能更多。但是这个数字完全取决于你的 c 库和 CPU。
330.1500249000119 包含 18 位数字,因此将四舍五入为 330.150024900012。330.15002490001189 只是一关,很好。通常你应该期望 1.189 对 1.2。
对于背后的确切数学,请尝试阅读 David Goldberg,“What Every Computer Scientist Should Know About Floating-point Arithmetic”,ACM Computing Surveys 23, 1 (1991-03), 5-48。如果您对细节感兴趣,这值得一读,但它确实需要计算机科学背景。 http://www.validlab.com/goldberg/paper.pdf
您可以通过使用更好的浮点类型(如 long double 或 __float128)或使用更好的 cpu(如 Sparc64 或 s390)来阻止这种情况的发生,它们在硬件中本机使用 41 位 (__float128) 作为 long double。
是的,使用 UltraSparc/Niagara 或 IBM S390 是一种文化。
通常的答案是:使用long double,伙计。这在 Intel 上为您提供了两个字节(18 位)和几个 powerpc(31 位),在 sparc64/s390 上为您提供了 41 个字节。