双精度,整型,数学.c# Round
本文关键字:Round 整型 双精度 数学 | 更新日期: 2023-09-27 18:14:21
我必须将双精度值x转换为两个整数,如下所示…
"x字段由两个带符号的32位整数组成:x_i代表整数部分,x_f代表小数部分乘以10^8。例如:x(80.99)将使x_i为80,x_f为99,000,000"
首先,我尝试了以下操作,但有时似乎会失败,当xF值应该是2000000时,xF值为1999999
// Doesn't work, sometimes we get 1999999 in the xF
int xI = (int)x;
int xF = (int)(((x - (double)xI) * 100000000));
下面的代码似乎在我测试过的所有情况下都有效。但我在想有没有更好的办法不用轮询。还有,是否存在这种方法仍然失败的情况?
// Works, we get 2000000 but there's the round call
int xI = (int)x;
double temp = Math.Round(x - (double)xI, 6);
int xF = (int)(temp * 100000000);
问题是:(1)二进制浮点数以精度换取范围;(2)某些值,如3.1 不能用标准二进制浮点数格式(如IEEE 754-2008)精确地表示。
首先阅读David Goldberg的"What Every Computer Scientist Should Know About Floating-Point Arithmetic",发表于ACM Computing Surveys, Vol 23, No . 1, 1991年3月。
请参阅以下页面,了解使用浮点数存储精确值的更多危险、缺陷和陷阱:
http://steve.hollasch.net/cgindex/coding/ieeefloat.htmlhttp://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
为什么要滚动你自己的when系统。小数能给出精确的小数浮点数吗?
但是,如果你要这样做,像这样做应该很好:
struct WonkyNumber
{
private const double SCALE_FACTOR = 1.0E+8 ;
private int _intValue ;
private int _fractionalValue ;
private double _doubleValue ;
public int IntegralValue
{
get
{
return _intValue ;
}
set
{
_intValue = value ;
_doubleValue = ComputeDouble() ;
}
}
public int FractionalValue
{
get
{
return _fractionalValue ;
}
set
{
_fractionalValue = value ;
_doubleValue = ComputeDouble() ;
}
}
public double DoubleValue
{
get
{
return _doubleValue ;
}
set
{
this.DoubleValue = value ;
ParseDouble( out _intValue , out _fractionalValue ) ;
}
}
public WonkyNumber( double value ) : this()
{
_doubleValue = value ;
ParseDouble( out _intValue , out _fractionalValue ) ;
}
public WonkyNumber( int x , int y ) : this()
{
_intValue = x ;
_fractionalValue = y ;
_doubleValue = ComputeDouble() ;
return ;
}
private void ParseDouble( out int x , out int y )
{
double remainder = _doubleValue % 1.0 ;
double quotient = _doubleValue - remainder ;
x = (int) quotient ;
y = (int) Math.Round( remainder * SCALE_FACTOR ) ;
return ;
}
private double ComputeDouble()
{
double value = (double) this.IntegralValue
+ ( ( (double) this.FractionalValue ) / SCALE_FACTOR )
;
return value ;
}
public static implicit operator WonkyNumber( double value )
{
WonkyNumber instance = new WonkyNumber( value ) ;
return instance ;
}
public static implicit operator double( WonkyNumber value )
{
double instance = value.DoubleValue ;
return instance ;
}
}
我认为使用小数可以解决问题,因为在内部它们确实使用十进制表示数字。使用double,将数字的二进制表示转换为十进制时会出现舍入错误。试试这个:
double x = 1234567.2;
decimal d = (decimal)x;
int xI = (int)d;
int xF = (int)(((d - xI) * 100000000));
EDIT:与RuneFS无休止的讨论表明,事情并不是那么容易。因此,我做了一个非常简单的测试,有一百万次迭代:
public static void TestDecimals()
{
int doubleFailures = 0;
int decimalFailures = 0;
for (int i = 0; i < 1000000; i++) {
double x = 1234567.7 + (13*i);
int frac = FracUsingDouble(x);
if (frac != 70000000) {
doubleFailures++;
}
frac = FracUsingDecimal(x);
if (frac != 70000000) {
decimalFailures++;
}
}
Console.WriteLine("Failures with double: {0}", doubleFailures); // => 516042
Console.WriteLine("Failures with decimal: {0}", decimalFailures); // => 0
Console.ReadKey();
}
private static int FracUsingDouble(double x)
{
int xI = (int)x;
int xF = (int)(((x - xI) * 100000000));
return xF;
}
private static int FracUsingDecimal(double x)
{
decimal d = (decimal)x;
int xI = (int)d;
int xF = (int)(((d - xI) * 100000000));
return xF;
}
在此测试中,51.6%的纯双精度转换失败,其中当数字首先转换为十进制时,没有转换失败。
有两个问题:
-
您的输入值很少等于小数点后8位的十进制表示。所以某种四舍五入是不可避免的。换句话说:你的数字
i.20000000
实际上会比i.2
稍微少一点或稍微多一点。 -
转换到
int
总是向零舍入。这就是为什么,如果i.20000000
小于i.2
,您将得到小数部分的19999999
。使用Convert.ToInt32
轮接近,这是你在这里想要的。它会在所有情况下显示20000000
所以,假设你所有的数字都在0
- 99999999.99999999
的范围内,下面的方法总是会给你最接近的解决方案:
int xI = (int)x;
int xF = Convert.ToInt32((x - (double)xI) * 100000000);
当然,正如其他人所建议的,转换为decimal
并使用它进行计算是一个很好的选择。