不使用字符串操作获取双精度数的小数部分

4
有没有一种方法可以在不使用字符串操作的情况下获取Double的小数部分?特别是在vb.net中?
各种语言中都有许多关于十进制的示例。VB.NET的一个示例就是这个 基本上,它们似乎都使用mod或Truncate方法,或者利用整数类型的转换行为。但是,由于double/float类型的固有不准确性,这些方法都无法适用于double/float类型。还存在doubles无法可靠地转换为decimals的问题。下面是一个测试用例,以展示我的意思和期望。
<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并不总是实际数字。我知道0.65 + 0.05≠0.7。但我也知道0.70000000000000007始终等于0.70000000000000007=0.65 + 0.05等等。但我想要的是小数部分,就像它看起来一样。)
我错过了什么吗?有没有一种可靠的方法来获取双精度浮点数的小数部分而不使用字符串操作?
如果必须使用字符串操作,如何才能使其始终有效,而不考虑文化格式?

编辑-测试结果符合我的要求。如果Double是0.70000000000000007,则希望该方法返回0.70000000000000007。如果是.65,则希望返回.65。由于.65 + .05 = 0.70000000000000007,因此GetFractionalParts(.65 + .05)应返回0.70000000000000007。

1个回答

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.69不等于6.9,这是因为double是一个近似值。请注意,10 * 0.696.9仅相差1个尾数增量。它们的小数部分之所以相差超过1个尾数增量,是因为IEEE 754 - 增量不是恒定的。接近0的值更准确,这是“不是错误,而是特性”,因为它们最常见。

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

如果您想打印出正确的值,则可以使用Jon Skeet的ToExactString方法: http://www.yoda.arachsys.com/csharp/DoubleConverter.cs


这基本上是其他人在处理小数时所做的。 - ElGringoGrande

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接