我正在使用C#(Unity3D兼容= .NET 2.0)Json库,我遇到了精确问题。首先,我有这个逻辑来解析数字字符串:
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from time import sleep
client = ModbusClient('ip_address_of_modbus_IO')
if(client.connect()):
print(client.read_discrete_inputs(200,1).bits[0])
client.write_coil(0,True)
sleep(100)
client.write_coil(2,True)
问题来了,因为对于大十进制数,十进制类型不是最好的类型。我有一个输出日志来显示问题(使用.ToString()):
...
string jsonPart ="-1.7555215491128452E-19"
enter code here
long longValue = 0;
if (long.TryParse(jsonPart, NumberStyles.Any, CultureInfo.InvariantCulture, out longValue))
{
if (longValue > int.MaxValue || longValue < int.MinValue)
{
jsonPartValue = new JsonBasic(longValue);
}
else
{
jsonPartValue = new JsonBasic((int)longValue);
}
}
else
{
decimal decimalValue = 0;
if (decimal.TryParse(jsonPart, NumberStyles.Any, CultureInfo.InvariantCulture, out decimalValue))
{
jsonPartValue = new JsonBasic(decimalValue);
}
}
...
但另一方面,这个十进制类型的例子是正确的:
String = "-1.7555215491128452E-19"
Float Parsed : -1.755522E-19
Double parsed : -1.75552154911285E-19
Decimal Parsed : -0.0000000000000000001755521549
我认为还有许多其他案例可以平衡到一种或另一种。 是否有任何聪明的方法来解析它失去最低精度?
答案 0 :(得分:1)
您看到的差异是因为,虽然decimal
可以容纳高达28或29位数的精度,而双倍的15或16位数,但其范围远低于double
。
A decimal has a range of (-7.9 x 10^28 to 7.9 x 10^28) / (10^(0 to 28))
A decimal
存储所有数字,包括小数点后面的零,前面有零(例如0.00000001) - 即它不使用指数格式存储数字。
A double has a range of ±5.0 × 10^−324 to ±1.7 × 10^308
double
可以使用指数格式存储数字,这意味着它不必将前导零存储在0.0000001之类的数字中。
这样做的结果是,对于位于小数范围边缘的数字,它实际上具有比双精度更低的精度。
例如,请考虑数字-1.7555215491128452E-19
:
将其转换为非指数表示法:
-0.00000000000000000017555215491128452
1 2 3
12345678901234567890123456789012345
你可以看到它的小数位数为35,超出了decimal
的范围。
正如您所观察到的那样,当您将该数字存储在decimal
后打印出来时,您会得到:
-0.0000000000000000001755521549
1 2
1234567901234567890123456789
根据微软的规范,只给你29位数字。
然而,double
使用指数表示法存储其数字,这意味着它不存储所有前导零,这使它能够以更高的精度存储该特定数字。
例如,double
将-0.00000000000000000017555215491128452
存储为具有15或16位精度的指数数字。
如果从上面的数字中取15位数的精度,你会得到:
-0.000000000000000000175552154911285
1
123456789012345
如果你这样做的确是打印出来的:
double d = -1.7555215491128452E-19;
Console.WriteLine(d.ToString("F35"));