我必须将双值x转换为两个整数,如下所示......
“x字段由两个带符号的32位整数组成:x_i表示整数部分,x_f表示小数部分乘以10 ^ 8.例如:x为80.99,x_i为80,x_f为99,000,000”
首先我尝试了以下内容,但有时似乎失败了,当它应该是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);
答案 0 :(得分:2)
问题是(1)二进制浮点交换范围的精度和(2)某些值,例如3.1 不能完全以标准二进制浮点格式(例如IEEE 754-)表示2008。
首先阅读David Goldberg的"What Every Computer Scientist Should Know About Floating-Point Arithmetic",发表于 ACM Computing Surveys ,第23卷,第1期,1991年3月。
然后查看这些页面,了解有关使用浮点数存储确切值的危险,陷阱和陷阱的更多信息:
http://steve.hollasch.net/cgindex/coding/ieeefloat.html http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
为什么在System.Decimal为您提供精确的十进制浮点时自己滚动?
但是,如果你要这样做,这样的事情就可以了:
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 ;
}
}
答案 1 :(得分:0)
我认为使用小数来解决问题,因为在内部他们确实使用了数字的十进制表示。使用double时,将数字的二进制表示转换为十进制时会出现舍入错误。试试这个:
double x = 1234567.2;
decimal d = (decimal)x;
int xI = (int)d;
int xF = (int)(((d - xI) * 100000000));
编辑:与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%的双精度转换失败,当数字首先转换为十进制时,没有转换失败。
答案 2 :(得分:0)
有两个问题:
您的输入值很少等于小数点后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
并将其用于计算是一个很好的选择。