将浮点数作为字符串进行比较!我可以这样做吗?这是有效的方法吗?

4
为了检查两个浮点数之间的差异是否为0.01,我这样做:
if ((float1 - float.Parse(someFloatAsStringFromXML).ToString(), System.Globalization.CultureInfo.InvariantCulture)).ToString() == "0,01000977")

这种"方法"可接受吗?是否有更好的方法?如何操作?

p.s. 我很新手,对C#和强类型语言不是很熟悉!如果你能讲得更详细一些,我会很感激!

我忘了提到,数字是“353.58”和“353.59”。它们以带点“.”而不是“,”的字符串形式出现,这就是我使用float.Parse的原因。


5
那一定是错误的做法。你实际想要做什么? - David Heffernan
这甚至不是有效的语法。为什么不将浮点数作为浮点数进行比较呢? - JeffRSon
如果您想进行精确比较,请使用Decimal,而不是字符串或浮点数。 - Lorenzo Dematté
选择 float 类型不应该是因为 "," 和 "." 的区别。 - Eren Ersönmez
只是出于好奇... float1someFloatAsStringFromXML 中存储了什么类型的数据?货币或数学值通常需要使用 decimal 类型,而 doublefloat 更适合于重量或长度等事物... 鉴于您永远无法测量某物的绝对精确长度或重量,因此检查两个值之间的差异是否为 0.01 在更基本的层面上是错误的(或者需要先进行四舍五入)。如果您正在比较财务数据,则应改用小数。 - MBender
显示剩余4条评论
4个回答

5

首先,你应该总是按照它们的底层类型(float、double、decimal),而不是按照字符串来比较数字。

现在,你可能会认为可以这样比较:

float floatFromXml = float.Parse(someFloatAsStringFromXML);

if (Math.Abs(float1 - floatFromXml) == 0.01)

我的示例的运作方式如下:

首先,计算两个值之间的差异:

float1 - floatFromXml

然后取它的绝对值(只是去掉了负号)。
Math.Abs(float1 - floatFromXml)

然后检查该值是否等于0.01:
if (Math.Abs(float1 - floatFromXml) == 0.01)

如果您不想忽略这个符号,那么就不需要使用 Math.Abs() 函数:

if ((float1 - floatFromXml) == 0.01)

但是这不适用于您的示例,因为存在舍入误差!
由于使用了浮点数(类似双精度浮点数),会存在舍入误差,这使得比较差异是否为0.01变得不可能。实际上,结果可能是0.01000001或其他略有偏差的值。
要解决这个问题,您必须将实际差异与目标差异进行比较,并且如果它们之间只有微小的差别,那么就可以接受。
在以下代码中,“target”是您寻找的目标差异。在本例中,它是“0.01”。
然后,“epsilon”是“target”可能出错的最小量。在此示例中,它是“0.00001”。
我们的意思是“如果两个数字之间的差异在0.1的0.00001以内,则我们将认为其匹配差异为0.1”。
因此,该代码计算两个数字之间的实际差异“difference”,然后查看它与“0.01”的距离,如果在“0.00001”以内,则打印“YAY”。
using System;

namespace Demo
{
    class Program
    {
        void Run()
        {
            float f1 = 353.58f;
            float f2 = 353.59f;

            if (Math.Abs(f1 - f2) == 0.01f)
                Console.WriteLine("[A] YAY");
            else
                Console.WriteLine("[A] Oh dear"); // This gets printed.

            float target = 0.01f;
            float difference = Math.Abs(f2 - f1);
            float epsilon = 0.00001f; // Any difference smaller than this is ok.

            float differenceFromTarget = Math.Abs(difference - target);

            if (differenceFromTarget < epsilon)
                Console.WriteLine("[B] YAY"); // This gets printed.
            else
                Console.WriteLine("[B] Oh dear"); 
        }

        static void Main()
        {
            new Program().Run();
        }
    }
}

不过,以下可能是你的答案

或者,你可以使用decimal类型代替浮点数,然后直接比较就可以(对于这个特定的情况):

decimal d1 = 353.58m;
decimal d2 = 353.59m;

if (Math.Abs(d1 - d2) == 0.01m)
    Console.WriteLine("YAY"); // This gets printed.
else
    Console.WriteLine("Oh dear");

问题中的代码试图检查差是否恰好为0.01。所以你的>=是错误的。 - David Heffernan
1
@DavidHeffernan 我猜他真正想要检查的是大于还是大于等于,但我会更新我的答案。 - Matthew Watson
@MatthewWatson 我想要的是精确的差异 - .01,所以David是正确的。我需要使用==而不是>=。 - featherbits
我编辑了我的问题。起初,它们都是字符串。然后使用float.Parse进行转换。 - featherbits
@ezi 好的,我已经更新了我的答案。使用浮点数无法获得精确的差异。我认为你应该使用decimal(它有一个你可以使用的Parse()方法,就像float一样)。 - Matthew Watson

3

你提供了两个数字的示例

353.58
353.59
这些数字无法在二进制浮点格式中完全表示。此外,值0.01也无法在二进制浮点格式中完全表示。请阅读《计算机科学家应该了解的浮点运算知识》
例如,当您尝试将353.58表示为float时,不存在具有该值的float,因此您会得到最接近的float值,该值恰好为353.579986572265625
在我看来,您正在使用错误的数据类型来表示这些值。您需要使用十进制格式。这将允许您准确地表示这些值。在C#中,您可以使用decimal类型。
然后您可以编写:
decimal value1 = 353.58m;
decimal value2 = 353.59m;
Debug.Assert(Math.Abs(value2-value1) == 0.01m);

使用 decimal.Parse()decimal.TryParse() 将您的数字文本表示转换为 decimal 值。

1
但不幸的是,您选择了错误的解决方案。使用与 epsilon 的比较是处理此问题的错误方法。 - David Heffernan
你能解释一下为什么在特定情况下需要使用 Math.Abs 吗? - featherbits
@DavidHeffernan 没问题;我只是在责备他没有完整地阅读我的答案! :) (今天我的声望已经达到上限,所以对我来说没有任何影响 ;) - Matthew Watson
我删除了那条评论,因为我不再使用 epsilon case。我接受了 David 的答案,因为有些细节更清晰。我不知道我会得到最接近的浮点值,它恰好是 353.579986572265625。基本上你们两个都是正确的。顺便说一句,我不是“她”,我是“他”,但无论如何,哈哈! ^^ - featherbits
好的,谢谢大家的帮助!;) - featherbits
显示剩余6条评论

0
直接回答你的问题,你能做到这个吗?可以。但这是一个有效的方法吗?几乎肯定不是。
如果你提供更多的背景信息,你会得到更详细的答案。

0

这不是一个好的做法。没有必要将其与字符串进行比较,也没有任何好处。应该将其与实际的浮点数值进行比较。

你的方法存在明显的问题,它不具备文化安全性。在英国,我们使用“.”而不是“,”作为小数点 - 因此.ToString()的结果根本不匹配。但除此之外,如果没有充分的理由,这样做只是一种不好的做法。


所以,.ToString()的结果完全不匹配,你能解释一下吗? - featherbits
如果我在浮点数上使用.ToString()方法,它会显示一个"."而不是一个",",例如"124.56"而不是"124,56"。由于您正在比较字符串,因此"124.56"与"124,56"不相等,即使它们表示相同的数字。 - PhonicUK
我不太确定我理解你的意思...所以"123.45" toString和"123,45" toString会有所不同吗? - featherbits
@chba Wikipedia有一篇很好的文章,介绍不同国家和文化如何在印刷中表示数字和货币。所以你会发现小数点可以是点或逗号。 如果你解析特定文化中设计用于印刷的文本字符串,你必须使用正确的方法和参数,例如在C#中Decimal.TryParse(..)有一个指定允许字符串模式的参数。 - subsci

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