双打的分数部分,不使用字符串操作

时间:2011-06-24 13:55:08

标签: vb.net double

有没有办法在不使用字符串操作的情况下获取Double的小数部分?特别是在vb.net?

在各种语言中有许多小数的例子。 One for vb.net is this one

基本上他们似乎都使用mod或Truncate方法,或者他们利用转换为整数行为。但由于double / float类型固有的不准确性,这些方法都不适用于double / float类型。还存在双打不能可靠地转换为小数的问题。下面是一个测试案例,以显示我的意思和期望。

<TestClass> _
    Public Class BasicNumberTests

        Function DecimalFractionalPart(ByVal number As Decimal) As Decimal
            Dim wholePart As Decimal = Math.Truncate(number)
            Return number - wholePart
        End Function

        Function DoubleFractionalPart(ByVal number As Double) As Double
            Dim wholePart As Double = Math.Truncate(number)
            Return number - wholePart
        End Function

        <TestMethod()> Public Sub SplitDoubleAsDecimal1()
            Dim number As Double = 0.65 + 0.05
            Dim fractionalPart As Double = CDbl(DecimalFractionalPart(CDec(number)))
            Assert.AreEqual(0.70000000000000007, number)
            Assert.AreEqual(0.70000000000000007, fractionalPart) '<- Fails
        End Sub

        <TestMethod()> Public Sub SplitDoubleAsDecimal2()
            Dim number As Double = 0.70000000000000007
            Dim fractionalPart As Double = CDbl(DecimalFractionalPart(CDec(number)))

            Assert.AreEqual(0.70000000000000007, number)
            Assert.AreEqual(0.70000000000000007, fractionalPart) '<- Fails
        End Sub

        <TestMethod()> Public Sub SplitDoubleAsDouble1()
            Dim number As Double = 1.65

            Assert.AreEqual(1.65, number)
            Assert.AreEqual(0.65, DoubleFractionalPart(number)) '<- Fails
        End Sub

        <TestMethod()> Public Sub SplitDoubleAsDouble2()
            Dim number As Double = 1.0 + 0.65
            Assert.AreEqual(1.65, number)
            Assert.AreEqual(0.65, DoubleFractionalPart(number)) '<- Fails
        End Sub

End Class

为了解决这个问题,我有一些使用字符串操作来获取小数部分的方法。这个问题是它只适用于某些格式化类型。

我在另一个地方(错误地)在另一个地方问了这个问题,并且(在我的问题被删除之前)有人说使用我不理解的双打。

(要清楚我确实意识到你看到的Double并不总是它看起来的实际数字。我知道.65 + .05&lt;&gt; .7但我也知道0.70000000000000007总是= 0.70000000000000007 = .65 + .05等等。但我希望得到小数的小数部分。)

我错过了什么吗?有没有办法在不使用字符串操作的情况下可靠地获得双打的小数部分?

如果我必须使用字符串操作,我该怎么做才能无论文化格式如何都能正常工作?

编辑 - 测试显示我想要的东西。如果Double是0.70000000000000007,我希望该方法返回0.70000000000000007。如果是.65我想要.65返回。自.65 + .05 = 0.70000000000000007后,GetFractionalParts(.65 + .05)应返回0.70000000000000007

1 个答案:

答案 0 :(得分:1)

怎么样:

REM Add: Imports System.Runtime.CompilerServices

<Extension()>
Public Function getFractionalPart(ByVal number As Single) As Single
    Return number - Math.Truncate(number)
End Function

<Extension()>
Public Function getFractionalPart(ByVal number As Double) As Double
    Return number - Math.Truncate(number)
End Function

<Extension()>
Public Function getFractionalPart(ByVal number As Decimal) As Decimal
    Return number - Math.Truncate(number)
End Function

疑难杂症

但请注意:

    Dim number As Double = 10 * 0.69
    Console.WriteLine(BitConverter.DoubleToInt64Bits(6.9D))
    Console.WriteLine(BitConverter.DoubleToInt64Bits(number))
    Console.WriteLine(BitConverter.DoubleToInt64Bits(6.9D.getFractionalPart()))
    Console.WriteLine(BitConverter.DoubleToInt64Bits(number.getFractionalPart()))

将打印出来:

    4619454727784602010
    4619454727784602009
    4606281698874543309
    4606281698874543304

原因是,10 * 0.696.9不同,这是因为double是近似值。另请注意,10 * 0.696.9仅相差1个尾数增量。它们的小数部分相差超过1个尾数增量的原因是IEEE 754 - 增量不是常数。接近0的值更准确,这不是一个错误,而是一个特征',因为你最常见。

我不确定你想要修复什么。

如果要打印correct值,则可以使用Jon Skeet的方法ToExactStringhttp://www.yoda.arachsys.com/csharp/DoubleConverter.cs